15

Building an Awesome Todo List App in React

If creating the Hello, World! example was a celebration of getting your feet wet with React, creating the quintessential Todo List app is a celebration of approaching React mastery. In this chapter, we tie together a lot of the concepts and techniques you’ve learned to create something that works as follows: https://www.kirupa.com/react/examples/todo.htm.

You start with a blank app that allows you to enter tasks for later (see Figure 15.1).

A figure shows a text field for entering a task. An "add" button is present next to the field.

Figure 15.1 A blank app with task entry.

The way this Todo List app works is pretty simple. You type in a task or item or whatever you want into the input field and then press Add (or click Enter/Return). After you’ve submitted your item, you’ll see it appear as an entry. You can keep adding items and have them all show up (see Figure 15.2).

Four different tasks: Sit at the bottom of apple tree, Avoid getting hit by falling apple, ???, and Explain gravity are listed below the text field.

Figure 15.2 You can add tasks and have them show up.

To remove an item, just click on an existing entry. Pretty simple, right? In the following sections, we build this app together from scratch using a lot of the adrenaline-inducing techniques you’ve learned so far.

A figure shows the chemical structure for adrenaline (aka Epinephrine).

This is going to be a fun exercise of building each part of the app and learning (in awesomely painstaking detail) how the various little things work along the way.

Onward!

Getting Started

The first step is to create a new React app, as you learned in Chapter 13, “Setting Up Your React Dev Environment Easily.” From your command line, navigate to the folder where you want to create your new project and enter the following:

Press Enter/Return to run that command. A few moments later, a brand new React project is created. We want to start from a blank slate, so we’re going to delete everything contained in our public folder and our src folder.

By now, you know the drill. You need a starting point, so go ahead and create a new HTML document inside your public folder called index.html. Inside it, add the following content:

This page is pretty basic, as you can tell. The real magic is going to be happening in the src directory, where your JavaScript and CSS files will live. In the src directory, create a new file called index.css and add the following style rules into it:

Now let’s add the JavaScript that rounds out the starting page. Within the same src directory, add a new file called index.js. Inside this file, add the following content:

Take a moment to look at what you’ve just added. By now, you should be fully familiar with what’s going on with the HTML, CSS, and JavaScript here. What we really have is the foundation. In the following sections, we’ll build on top of this all the pieces that make up the rest of our Todo List app.

Creating the Initial UI

Right now, our app doesn’t do a whole lot. It doesn’t look like much, either. We’ll deal with the functionality in a little bit, but first let’s get the various UI elements up and running. That isn’t very complicated for our app. First we’ll get our input field and button to appear. This is all done by using the div, form, input, and button elements.

All of that will live inside a component we’ll call TodoList. In your src folder, add a file called TodoList.js. Inside this file, add the following:

Take a moment to glance at what you’ve added. You can see a bunch of JSX that gets the form elements up and running. To use the newly created TodoList component, let’s go back to index.js and reference it to see how our app looks now. Go ahead and make the following two changes:

Save all your changes and preview in your browser. If everything worked, you’ll see something that looks like Figure 15.3.

The app screen shows a to-do list. A text field for entering a task and an add button next to it are shown.

Figure 15.3 What our app looks like right now.

Right now, we have our input field and submit button showing up. These two UI elements neither work nor look too visually appealing. We’ll fix that in a little bit, but first let’s talk about how we’re going to add the rest of the app’s functionality.

Building the Rest of the App

As you can imagine, getting the initial UI elements to show up is the easy part. Tying up all the visuals with the underlying data is where the real work lies. This work can roughly be divided into five parts:

1. Adding items

2. Displaying items

3. Styling

4. Removing items

5. Animating items as they are added or removed

Individually, all of these little implementation details are easy to wrap your brain around. When you put them together, you need to watch out for a few things. We’ll look at all that and more in the following sections.

Adding Items

The first major task to tackle is setting up the event handlers and default form-handling behavior to allow us to add an item. Go back to the form element and make the following highlighted change:

We listen for the submit event on the form itself, and we call the addItem method when that event is overheard. Notice that we aren’t listening for any event on the button itself. This is because our button has a type attribute set to submit. This is one of those HTML trickeries in which clicking on the button whose type is submit is the equivalent of firing the submit event on the form.

Now it’s time to create our addItem event handler that will get called when our form gets submitted. Add the following highlighted lines just above where we have our render function defined:

All we did was define our addItem event handler and ensure that the keyword resolves properly. We still haven’t done anything remotely close to actually adding a task, so let’s start by first defining our state object in the constructor:

Our state object isn’t very complicated. We’re just defining an items array/property that will be responsible for storing the various items that you can enter. All that’s left to do now is read the entered value from our input element and store it in our items array when the user submits it. The only complication here is actually reading the value from a DOM element. As you know, React puts up a gate between us and the DOM. It doesn’t like to have us accessing DOM elements and fiddling with properties on them, but it does give us a loophole via refs that we can use.

In our render function, make the following highlighted change:

Here we’re storing a reference to our input element in the appropriately named _inputElement property. To state this differently, anywhere inside this component where we want to access our input element, we can do so by accessing _inputElement. Now it’s time to fill out our addItem function with the following content:

Take a moment to look through what we’re doing. We create a variable called itemArray to store the current value of our items state object. Next, we check to see if our input element has any content inside it. If it’s empty, we don’t do anything. If our input element has some text entered, we add that text to our itemArray:

We aren’t just adding the entered text. We’re actually adding an object that contains both the entered text and a unique key value that’s set by the current time (Date.now()). If you aren’t clear on why we’re specifying the key, that’s okay. You’ll totally see why in a few moments.

The rest of our code is pretty boring. We’re setting our state’s items property to the value of itemArray. We’re clearing the value of our input element to make room for the next todo item. This line here might be less boring:

We’re overriding this event’s default behavior. The reason has to do with how form submission works. By default, when you submit a form, the page reloads and clears everything out. We definitely don’t want that. By calling preventDefault, we block the default behavior. That’s a good thing!

It’s time to take stock of where we are right now. If you preview your app and check the browser console, you’ll see our state object correctly populating with each new todo item we added (see Figure 15.4).

The console view shows the stored entries.

Figure 15.4 We can now see the entries being stored. I realize that this might not seem like much, but we’re making great progress—seriously!

Note

For an alternate approach for setting new state inside the add. This method without modifying the existing state, check out this version: http://bit.ly/setStateConcat.

Displaying the Items

Having our todo items show up only in the console might be exciting for some of your users, but I’m pretty certain that most probably want to see these items displayed directly on the page. To do this, we’re going to rely on another component. To get started, let’s call this component TodoItems, specify it in our TodoList component’s render method, and pass in our items array as a prop.

All of that translated into markup and code looks as follows:

After you’ve done this, add the import statement to the top of the document as well:

These two changes wrap up the work we want to do in TodoList.js for now. Next, let’s go ahead and actually create our TodoItems component. In the src directory, create a new file called TodoItems.js and add the following content into it:

This might look like a lot of code to add in one giant swoop, but take a moment to look at what exactly you’re adding. In our render function, we’re taking the list of todo items (passed in as entries) and turning them into JSX/HTML-ish elements. We do that by calling map on our items and relying on the createTasks function:

The value stored by our listItems variable is an array of li elements that contain the appropriate content to print. Notice that we’re setting the key attribute—whose value, as you recall, we set earlier using Date.now()—on each element, to make it easier for React to keep track of the elements.

We turn this list of elements into something we can show onscreen with the following:

After you’ve made this change, save all the changes and preview the app in its current state (npm start if it isn’t already running). If everything worked properly, not only will you be able to add items, but you also will be able to see them (see Figure 15.5).

The entries: "Hi everybody, " "looks like it," "is this working?" appear under the text field in the to-do list app screen.

Figure 15.5 Our entries are now showing up!

If what you see looks similar to the figure, that’s awesome! To celebrate, let’s take a little break from looking at JS and JSX.

Styling our App

Right now, our app’s awesome functionality isn’t reflected in how the app currently looks. We’re going to fix this easily by adding one stylesheet and putting all the relevant style rules into it. In the src folder, create a new stylesheet called TodoList.css and add the following style rules into it:

After you’ve created this stylesheet, you need to reference it. In TodoList.js, add a reference to this stylesheet at the top:

If you preview your app after this change, it will look as shown in Figure 15.6.

Three entries: "hola," "hello," and "hi" appear in rectangular sections below the text field in the app screen. The entries appear neat and organized.

Figure 15.6 Our app is starting to look much nicer.

As you can see, our app looks much nicer. All we did is add some CSS, so from a functionality point of view, nothing has changed. We’ll make more progress on functionality next.

Removing Items

At this point, we can add items and see them appear. What we can’t do is remove items after they’ve been added. We’re going to allow users to remove items by clicking on them directly. This seems straightforward to implement, right? The only thing to watch out for involves where to put all our code. The items we click on are defined in TodoItems.js. The actual logic for populating the items lives in our state object in TodoList.js. To give you a preview of what to expect, we will be partaking in some shenanigans as we pass things between both of those components.

First we need to set up the event handler for dealing with the click event. Change the return statement under createTasks to look as follows:

We’re simply listening to the click event and associating it with an event handler called delete. What might be new is our approach for passing arguments to the event handler. Because of how event arguments and event handlers deal with scope, we work around all those issues using an arrow function that allows us both to maintain the default event argument and pass in our own arguments. If this seems bizarre, you can feel better knowing that this is a JavaScript quirk and has nothing to do with React.

After you’ve made this change, you need to define the delete event handler. Make the following highlighted changes:

Here we define a function called delete that takes our argument for the item key. To ensure that this resolves properly, we explicitly bind this in the constructor. Notice that our delete function doesn’t actually do any deleting. It just calls another delete function passed into this component via props. We’ll work backward from here and deal with that next.

In TodoList.js, take a look at our render function. When calling TodoItems, let’s specify a prop called delete and set it to the value of a function called deleteItem:

This change ensures that our TodoItems component now has knowledge of a prop called delete. This also means that our delete function we added in TodoList actually connects. All that remains is actually defining our deleteItem function so that it can deal with deleting an item.

First, go ahead and add the deleteItem function to your TodoList component:

You can add it anywhere, but my preference is to put it just below where our addItem function lives. Take a look at what this code does. We are passing the key from our clicked item all the way here, and we check this key against all the items we’re storing currently via the filter method:

The result of running this code is simple. We create a new array called filteredItems that contains everything except the item we are removing. This filtered array is then set as our new items property on our state object:

Our UI then updates and the removed item disappears forever. The last thing we need to do is deal with the usual shenanigans surrounding this. Make the following change in the constructor:

This ensures that all references to this inside deleteItem will reference the correct thing. Now we have just one more thing to do before we can declare victory in deleting items. Open TodoList.css and make the following highlighted change and style rule addition:

This provides the hover effect when you move the mouse cursor over the item that you want to remove. With this change done, our functionality to remove an item should be complete. Preview your app now and try adding some items and removing them. It should work well. And now on to just one more thing….

Animation! Animation! Animation!

Our very last task is to add some animations, to make adding and removing items look more natural. React offers many ways to animate something. You can use traditional approaches such as CSS animations, CSS transitions, requestAnimationFrame, the Web Animations API, or even a popular animation library. All of these approaches will take you far…very far.

When it comes to animating the existence of an element, though, the traditional approaches we outlined run into some limitations. This is because React entirely handles the lifecycle of an element as it is about to be deleted from the DOM. We can definitely override some of the lifecycle methods to intercept an element deletion and interject our own animation logic, but that gets us a bit too far into the weeds. We don’t want to deal with that right now.

Fortunately, the React community has come up with a handful of lightweight animation libraries that make animating adding and deleting elements really easy. One such library is Flip Move. Among many things, this library makes animating the addition and removal of list elements simple.

To use this library, we need to first add it to our project. From the command line, make sure you are still in the same location as our todolist project and run the following command:

Click Enter/Return to copy all the necessary things locally into our project’s node_modules folder. That’s all the setup required. After you’ve done this, in TodoItems.js, add the following import statement at the top:

Now all that’s left is to tell our FlipMove component to animate our list of items. In our render function, make the following highlighted change:

We’re simply wrapping our listItems (just before getting them printed) inside a FlipMove component and specifying the animation duration and the type of easing function to use. That’s it. If you preview your app now, you’ll see that adding and removing items doesn’t just suddenly happen; these items are smoothly animated instead.

Uncontrolled Components vs. Controlled Components

Form elements are interesting. These are elements that contain some state on their own. For example, your text element might have some content in it, or you might have some items already selected in a drop-down list. React is all about centralizing state into its own little world, so it doesn’t like that form elements have their own internal mechanism for storing state. The guidance is to synchronize all the form data inside a React component by using events such as onChange. These components that let React deal with form elements are known as controlled components.

Still, it’s a hassle to have every form element deal with keeping state in sync. The React developers get that as well. The workaround is to do nothing. We simply let form elements deal with their own state and use refs to access the values when needed. That’s what we did in this example. When we have components that defer all state management to the form DOM element, these components are known as uncontrolled components.

Conclusion

Our Todo app is pretty simple in what it does, but by building it from scratch, we covered almost every little interesting detail React brings to the table. More important, we created an example that shows how the various concepts we learned individually play together. That’s truly actually the important detail.

Now here’s a quick question for you: Does everything we’ve done in this chapter make sense? If so, you’re in good shape to tell your friends and family that you’re close to mastering React. If you still find some areas confusing, I recommend that you go back and reread the chapters that address your shortcomings.

Note: If you run into any issues, ask!

If you have any questions or your code isn’t running like you expect, don’t hesitate to ask! Post on the forums at https://forum.kirupa.com and get help from some of the friendliest and most knowledgeable people the Internet has ever brought together!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.226.169