A Committed Intro Guide to ReactJS, Part 3
Welcome back to our app-building endeavor with ReactJS! In part 1, we bootstrapped a new app and acquainted ourselves with stateful components. In part 2, we delved right into React development. We used the lifecycle hook componentDidMount
and we created an action to fetch data asynchronously and render a list of todos! Lastly, we refactored code to make use of stateless functional components.
There is a dedicated Github repo for this blog series. The commit history follows the steps taken in this guide. Feel free to fork the repo and checkout
any commit to experiment along with this series!
We’re working right in the file src/App.js
, which stands like this before we begin here in part 3. We have a little list of feature requirements, of which we already took care of the first two items.
Requirements:
X) display a loading text (while “fetching” todos) — done!
X) load and display todos
3) add new todos
4) toggle todos done/undone
5) delete todos
We’ve acquired much of the knowledge we need to tackle the last three features. Let’s begin with adding new todos…
Adding New Todos
We need to accept input from the user creating new todos.
Creating a Form
Let’s build a Form
component that will have a submit button, and a text input
from which we will process new todo “titles”. Let’s sketch out such a form:
We’ve prepped the Form
component, and the App
(parent) component passes it all of its state
and actions
right there on line 38.
We could also pass down individual props (as Form
is only using one prop thus far) for example:<Form submit={this.actions.submit} />
,
but this “prop drilling” gets tedious because you have to keep updating the parent component as you build the children components. For now, as we’re prototyping, I suggest we pass everything down by default; we can move faster and not to worry if the prop will be there in the child component. If prop drilling drives you mad, look into context, which was made official in React 16.3)
So, back to our little code review of our Form
component. It has a onSubmit
handler, which calls e.preventDefault
to prevent an unwanted page reload from the browser, and then calls the submit
action… that we still need to write, on line 16.
Controlled Input
Before we can submit the form however, we need to use that text input
to get a todo title
from the user. We’ll wire up a controlled input, meaning that we keep track of (and control) the value of our input
from the state up top. Let me just show you what I mean:
Voila, we add a newTitle
state (line 12) which is tied to the value of the form, on line 60. To change its value, we call our newly created action changeTitleInput
on line 62 which does a setState
on newTitle
on line 17. Our controlled input is all wired up! The new title input is now on our state and we can access it from the root component!
Submitting the Form
All of a mere sudden, actions.submit
has all that it needs because it can just look on this.state
for the title input. Let’s tie up our implementation then:
In the submit
action, we call on our mock db
to add a new todo to the database (which will callback our setState
function from syncTodos
, if you remember from part 2) so we’ll have that new todo just pop up on the screen. Hooray!
Also, we reset the input
field on line 25 with:this.setState({newTitle: '' })
so the input is empty again right after the user has submitted the form.
Finally, on line 70 we’re disabling the button when the input is empty, otherwise we’re making it possible to create empty todos which is not desirable. Here is what it looks like in the browser:
Destructuring Hipness
We’re done with “adding new post”, but let’s do one small (esthetic) enhancement; let’s destructure the props of our new Form
component, so our code isn’t as repetitive:
We no longer have props
all over the component and fewer “.
chaining” in our JSX (props.submit
becomes just submit
). It’s also clearer what props we’re using in the rendering without scanning the entire component. The component is making use of newTitle
, changeTitleInput
, and submit
as we can see at the top of the function. (Note that we can use this syntax because it’s been bundled for us via Babel and create-react-app from part 1 of this guide).
Sweetness! Let’s commit our changes. It may seem like a work of work but in the end we’ve only added 34 lines of code in this commit.
I’ve only destructured our new Form
component above. Can you do the same for the Todos
component?
Toggling Individual Todos
Let’s ride the wave… and tackle our next task: the toggling of individual todos as completed (done) or not. We’ll start by adding a controlled checkbox input
for each mapped todo:
We’re adding a checkbox input
for each todo in our list. We’re “controlling” it via the todo’s done
property, which is always initiated in the mockDB as false
. Let’s create a new action to update the appropriate done
key to its opposite (from false
to true
or vice-versa):
The new action toggle
on line 16 just needs the id
of the todo, as it looks up its current done
status on this.state
, flips it with !
, and updates the DB. The DB change triggers a new setState as part of our prior componentDidMount
setup (see part 2 of this guide).
On line 55, we have to remember to do a little “drilling” with our actions: we pass all the actions
down to the Todos
component (because it’s now using the action toggle
). Then we simply attach the toggle
to the controlled checkbox via its onChange
handler on line 88.
And we’re all done with the toggling!
A Touch of Style
It would be nice to have more visual feedback when a todo is done. Right now we only see the checkbox itself as an indication, which makes it a bit hard to tell if it’s even connected to the todos
state at this point.
Let’s cross out an item when it is “done” by conditionally adding this CSS rule: text-decoration: line-through
, so our UI end up like this:
We’ll take the opportunity to use CSS-IN-JSS, a technique popularized by React where you define CSS within the JS and basically abandon CSS files, (a bit similar to how we “handle HTML” in JS with React in fact). It looks like this in practice:
We can use the style
property of an element and pass it an object (within a JS expression — that’s why the double bracket style={{ ... }}
) . Each objet key is a CSS property, but you have to be careful to camel case it: the CSS text-decoration
becomes textDecoration
. Then we use the JS logical operator &&
to add the line-through
when done is true
. Now we have conditional styling.
Please note this implementation of CSS-IN-JS is rather naive and limiting. For example, you can’t use selectors like :hover
. However, if you like this approach, I recommend you check out Styled Components.
Furthermore, check out the multitude of UI-Kits out there to style your React app. Perfect if you don’t have a designer onboard yet.
Deleting A Todo
Well, well… I leave this one up to you, partner! Why not send me a pull request on Github? Consider it a challenge. If you can complete the task, you’ll be well on your way to being a React dev!
Hint: you’ll be making use of db.deleteTodo
which takes a todo id
as a parameter. Good luck!
[update]: A PR has been merged. (Thanks Nick!)
The Bittersweet End
We’re arrived at the end of this guide. I hope you’ve enjoyed the content, and that you now feel comfortable with the key aspects of React development!
If we were to continue, what would you like to do next: add some tests? integrate a backend? add more features? Feel free to leave a comment. Feedback is always appreciated.
I’m also on Twitter.
Photo Credit
“A MacBook with lines of code displayed on its screen next to a mug” by Artem Sapegin