React Hooks Tutorial: build a to-do list with React Hooks
Tags: react, frontend, hooks, javascript
Published: 2020-10-13 16:08:50

*This article was written by Christina Kopecky. React hooks are a powerful tool for building beautiful, simple apps. Learn how to build an interactive to do list with the `useState()` React hook.*

___

With the release of [React 16.8](https://www.educative.io/blog/the-evolution-of-react) in 2019, **React Hooks** have finally become available to use in our production applications. This allows React developers to make functional components stateful. Instead of using a class component to hold stateful logic, we can use functional components. 

React Hooks are a powerful tool, so, to get you started, today we will **build a to do list** with this modern tool. By the end, you'll have your first React application with hooks! 

*It is assumed you already know at least the basics about React. If you’re a React beginner, that’s okay. Check out our [React beginners tutorial](https://www.educative.io/blog/what-is-react-tutorial) before continuing here.*

**Today, we’ll take a look at:**
* [Overview of React hooks](#overview)
* [To Do List Project Prompt](#prompt)
* [To Do List: Step-by-Step Guide](#guide)
* [Complete To Do List code](#code)
* [What to learn next](#next)

<br>

<br>

<a name="overview"></a>

## Overview of React hooks

In React, hooks are functions that allow you to *hook into* React state and lifecycle features from function components. This allows you to use React **without classes**. 

When you take an initial look at the React Hooks documentation, you’ll see that there are several Hooks that we can use for our applications. You can even create your own. Some of the popular ones include:
* `useState`: returns a stateful value
* `useEffect`: perform side effects from function components
* `useContext`: accepts a context objects and returns current context value 
* `useCallback`: pass an inline callback and an array of dependencies 


> **Some benefits of hooks are:**
>* Isolating stateful logic, making it easier to test
>* Sharing stateful logic without render props or higher-order components
>* Separating your app’s concerns based on logic
> * Avoiding ES6 classes


The only hook we will need for this particular to do list project is **`useState()`**. This hook replaces the need for a state object in a class component structure. 

When looking at older React legacy code, most likely you will see something like the following: 

```js
import React from 'react';
import './App.css';
 
class App extends React.Component {
 constructor(props) {
   super(props);
   this.state = {
     movies: [ "Star Wars", "Return of the Jedi", "Empire Strikes Back" ]
   }
 }
 
 render() {
   return (
     <div>
       {this.state.movies.map(movie => {
         return (
           <div key={movie}>
             {movie}
           </div>
         )
       })}
     </div>
   )
 }
}
 
```

The class component structure describes an instance of an `App` object that has state that is an array of movies. We render that array of movies by mapping over the state object and returning a single movie for each one in the array. 

Stateful functional components are very similar in that they hold state, but they are much simpler. Take the following: 


```js
import React, { useState } from 'react';
import './App.css';
 
function App() {
 const initialValue = [
   "Star Wars", "Return of the Jedi", "Empire Strikes Back",
 ]
 
 const [ movies, setMovies ] = useState(initialValue);
 
 return (
   <div>
     {movies.map((movie) => {
       return <div key={movie}>{movie}</div>;
     })}
   </div>
 );
}
 
export default App;
```

The `useState` hook is deconstructed into an array with two items in it: 
* The **variable** that holds our state (`movies`)
* A **method** that is used to update that state if you need to (`setMovies`)

Now that you have the basic idea behind the `useState` React Hook, let’s implement it when creating a To Do List Application! 

<br>

<a name="prompt"></a>

## To Do List: Project Prompt

Our goal is to create a To Do List UI. This UI will have three main components:
* **Header** that labels the To Do list. This is just a basic application identifier 
* A **list** to display each to do item. 
* A **form** that adds a To Do task item to the list. The default complete should be set to `false`.

> For our list, there are two additional capabilities that we need to create:
> * Give the ability to *toggle on and off* a strikethrough that indicates completion. 
> * At the bottom of the list will be a component that will prompt the user to *delete the completed tasks*. This can be a button or some other kind of call-to-action. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jmyqxs5satgec226iri2.png)

<br>

<a name="guide"></a>

## Step-by-Step guide to creating a to do list

<br>

### 1. Create a React application
* **yarn:** `yarn create react-app todo-list`
* **npm:** `npx create-react-app todo-list`

`cd` into todo-list and run `yarn start` (if using Yarn) OR `npm start` (if using npm). Your project should now be served on `localhost:3000`.

<br>

### 2. App.js

Navigate to `App.js` and get rid of everything between the two `<div>` tags. We won’t need any of the pre-populated code. Our `App.js` is pretty bare bones at this point: 

```js
import React from 'react';
import './App.css';
 
function App() {
 return (
   <div className="App">
    	Hello World! A To Do List Will Go here!
   </div>
 );
}
 
export default App;
```

> If you want to add a string or something to make sure your React instance is working on localhost, feel free to do so. 

<br>

### 3. Header
Create a new file in the `src` directory and name it `Header.js`. Then, Create a presentational component that will display a header identifying the name of your application. Export your Header and import it to `App.js`. In the empty `<div>`, add `<Header />`.

**Here is our code so far:**

*App.js*

```js
import React from 'react';
 
//components
import Header from "./Header";
 
import './App.css';
 
function App() {
 return (
   <div className="App">
     <Header />
   </div>
 );
}
 
export default App;
```

*Header.js*

```
import React from 'react';
 
const Header = () => {
   return (
       <header>
           <h1>To Do List</h1>
       </header>
   );
};
 
export default Header;
```

<br>

### 4. Create mock data to test application

Copy and paste this JSON object into a new file in the `src` directory named `data.json`. We are going to work with this data in our project to test to see if things are working properly. 

In `App.js`, add `import data from “./data.json”;` to your list of imports at top of page. 

```js
[{
   "id": 1,
   "task": "Give dog a bath",
   "complete": true
 }, {
   "id": 2,
   "task": "Do laundry",
   "complete": true
 }, {
   "id": 3,
   "task": "Vacuum floor",
   "complete": false
 }, {
   "id": 4,
   "task": "Feed cat",
   "complete": true
 }, {
   "id": 5,
   "task": "Change light bulbs",
   "complete": false
 }, {
   "id": 6,
   "task": "Go to Store",
   "complete": true
 }, {
   "id": 7,
   "task": "Fill gas tank",
   "complete": true
 }, {
   "id": 8,
   "task": "Change linens",
   "complete": false
 }, {
   "id": 9,
   "task": "Rake leaves",
   "complete": true
 }, {
   "id": 10,
   "task": "Bake Cookies",
   "complete": false
 }, {
   "id": 11,
   "task": "Take nap",
   "complete": true
 }, {
   "id": 12,
   "task": "Read book",
   "complete": true
 }, {
   "id": 13,
   "task": "Exercise",
   "complete": false
 }, {
   "id": 14,
   "task": "Give dog a bath",
   "complete": false
 }, {
   "id": 15,
   "task": "Do laundry",
   "complete": false
 }, {
   "id": 16,
   "task": "Vacuum floor",
   "complete": false
 }, {
   "id": 17,
   "task": "Feed cat",
   "complete": true
 }, {
   "id": 18,
   "task": "Change light bulbs",
   "complete": false
 }, {
   "id": 19,
   "task": "Go to Store",
   "complete": false
 }, {
   "id": 20,
   "task": "Fill gas tank",
   "complete": false
 }]

```


<br>

### 5. Read list of to dos and display

The next thing we need to do is test our ability to read a set of test data. Let’s use our `useState()` hook to wire up some local state in `App.js`.

**Basic syntax for `useState()`**

```
const [ variable, setVariable ] = useState(<initState?>); 
```

> **Note:** Remember to `import React, { useState } from ‘react’;` at the top of the page.


```js
import React, { useState } from 'react';
import data from "./data.json";
//components
import Header from "./Header";
 
import './App.css';
 
function App() {
  const [ toDoList, setToDoList ] = useState(data);
 
 
 return (
   <div className="App">
     <Header />
   </div>
 );
}
 
export default App;
```


Now we need to map over the `toDoList` and create individual todo components. Create two new files in the `src` directory called `ToDoList.js` and `ToDo.js`. 

The `ToDoList.js` file is the container that holds all of our todos, and `ToDo.js` is one single row in our To Do List. 

Don’t forget to export the `ToDoList` and import it to `App.js`. Also, export the `ToDo` and import it into `ToDoList.js`. We will need it when we return our individual components in our map function. 

```
import React, { useState } from 'react';
import data from "./data.json";
//components
import Header from "./Header";
import ToDoList from "./ToDoList";
 
import './App.css';
 
function App() {
  const [ toDoList, setToDoList ] = useState(data);
 
 
 return (
   <div className="App">
     <Header />
     <ToDoList toDoList={toDoList}/>
   </div>
 );
}
 
export default App;
```

Because our state logic is held in `App.js` (it will become clearer soon why that is), we need to pass our entire `toDoList` down to our `<ToDoList />` component. 

In our ToDoList, we will map over the `todoList` object that was passed down as props to create individual todos. Don’t forget to pass down the individual todo down as props to the ToDo component. 

```js
import React from 'react';
import ToDo from './ToDo';
 
 
const ToDoList = ({toDoList}) => {
   return (
       <div>
           {toDoList.map(todo => {
               return (
                   <ToDo todo={todo} />
               )
           })}
       </div>
   );
};
 
export default ToDoList;
```

All we want from the ToDo component is the actual task that is on our list. We will also need to make use of the `complete` property on the todo object to indicate whether or not something is decorated with a strikethrough.

```js
import React from 'react';
 
const ToDo = ({todo}) => {
   return (
       <div>
           {todo.task}
       </div>
   );
};
 
export default ToDo;
```

> ## Keep the learning going.
> Learn React Hooks and advanced React concepts without scrubbing through videos or documentation. Educative's text-based courses are easy to skim and feature live coding environments, making learning quick and efficient. 
> 
> <b>[The Road to React: The one with Hooks](https://www.educative.io/courses/road-to-react-with-hooks)

### 6. Toggle task completion 
Let’s tackle toggling on and off whether or not a task is completed. 

Let’s first add a `className` to our individual ToDo component that will help us with styling. We are going to use a little bit of JavaScript to help us here. Here we add the attribute `className` and set it equal to a JavaScript expression that asks the question whether or not the todo is complete. 

> **Note:** Anything in between curly braces when using JSX signals that we are using JavaScript.




```js
 const ToDo = ({todo}) => {
   return (
       <div className={todo.complete ? "strike" : ""}>
           {todo.task}
       </div>
   );
};
```

If our task is completed, we will use the className `strike` to enforce styling. Otherwise, there won’t be a `className`. In our `index.css`, add the following:

```js
.strike {
   text-decoration: line-through;
}
```

Now if you were to take a look at your React application, you should see some of the tasks with a line through it indicating that a project or task has been done. 

Next, we have to create a function that will toggle the complete from true to false. This requires going back to `App.js` since our state resides there. 

Creating a **toggle function** (`toggle()`) is fairly simple. What we want to do is that when a user clicks on a task, we want to change the state of complete to true if it’s false or vice versa. We will use the second variable in our deconstructed `useState` array to do this. 

```js
 const handleToggle = (id) => {
   let mapped = toDoList.map(task => {
     return task.id == id ? { ...task, complete: !task.complete } : { ...task};
   });
   setToDoList(mapped);
 }
```

For this function, I passed in the id of the item that was clicked. Mapping over the `toDoList` creates a new array. We can find the id of the current target and then flip the task to complete or not complete depending on the Boolean already passed in. 

> `setToDoList(mapped)` is analogous to `this.setState({ toDoList: mapped })`, which was used when we worked with state in class components. 

You can now toggle on and off tasks that are completed!


<br>

### Delete completed tasks 

What are we going to do with all of those crossed off, completed tasks? Let’s delete them! Create a **button** that will have an `onClick` handler that filters out all of the completed items. 

This is super similar to the `handleToggle` function we just did. All we need to do is take the `toDoList` and filter through it, return all items that are not completed, and then set the filtered array onto `toDoList`. 

Because the filter method returns a new array, we are not in danger of mutating state and can proceed without making a copy of the array before we play with it. 


```js
const handleFilter = () => {
   let filtered = toDoList.filter(task => {
     return !task.complete;
   });
   setToDoList(filtered);
 }
```

Then, add a button to the end of the `ToDoList` component and set an `onClick` to fire the `handleFilter` function. You want to be certain to add your `handleFilter` function to `App.js` and then pass down the function as props to the `ToDoList`.

```js
import React from 'react';
import ToDo from './ToDo';
 
const ToDoList = ({toDoList, handleToggle, handleFilter}) => {
   return (
       <div>
           {toDoList.map(todo => {
               return (
                   <ToDo todo={todo} handleToggle={handleToggle} handleFilter={handleFilter}/>
               )
           })}
           <button style={{margin: '20px'}} onClick={handleFilter}>Clear Completed</button>
       </div>
   );
};
 
export default ToDoList;
```

<br>

### 8. Add tasks with form component

The final item on our list is to create a form component that will handle adding tasks to our `ToDoList`. Create a new file in your `src` directory and call it `ToDoForm.js`. 

Create a basic form that will allow for a user to input a task name, hit enter or click on a button, and have a function fire to add the task. For a form to work correctly we have to keep track of the changes as we go, so logically we have to handle what happens as the input changes. 

**Form Logic** 

There are four main things that we need to have to make our forms work: 
* Local state (so we will need to employ the `useState()` hook)
* Our form component with an input value that is assigned to the correct variable
* A function that handles the state’s changes
* A function to handle the form submission

<br>

**`useState` to handle user input**

Add an import for the `useState` hook to your React import. Our state here will keep track of any input that the user types into their form. The initial state is set to an empty string since there should be nothing in the form yet. 
  
```
const [ userInput, setUserInput ] = useState('');
```

<br>

**Form Component**

Now, create a form component that encapsulates an input and a button. Fairly basic. You can play with style later. 

<br>


**`Input.value`**

Your `<input>` element should have a value associated with it that matches the name of your state variable (I named mine `userInput`). The change handler will take the value here and set the state every time it changes. 

```
<input value={userInput} type="text" onChange={handleChange} placeholder="Enter task..."/>
```

<br>

**`handleChange`**

This is the function that will handle the local state’s changes. Every time a user types in the input box, the state will change to reflect the most recent input. 

```
const handleChange = (e) => {
       setUserInput(e.currentTarget.value)
   }
```

<br>

**`handleSubmit`**

When a user hits ‘Enter’ or clicks the ‘Submit’ button, this function will fire to add the task to the `toDoList` array. 

```
const handleSubmit = (e) => {
       e.preventDefault();
       addTask(userInput);
       setUserInput(“”);
 
   }
```

When we use forms, remember to use `e.preventDefault()` because we don’t want the default action to take place. In this case, it would reload the page and everything changed will go back to how it initially rendered. 

Be sure to set `userInput` back to an empty string after the `addTask` function has run. This will set the form back to an empty input.

<br>

**`addTask`**
Next is the addTask function. This function goes in `App.js` since that is where all of our `toDoList` state is. We need to be able to set the new array on state using `setToDoList` and we can only do that when the `addTask` function has access to that state. 
 
```
 const addTask = (userInput) => {
   let copy = [...toDoList];
   copy = [...copy, { id: toDoList.length + 1, task: userInput, complete: false }];
   setToDoList(copy);
 }
```

This function takes in userInput that we gathered from our form component’s state. Make a copy of the `toDoList` so we don’t directly manipulate the state. 

Next, reassign copy to a new array, with copy spread in and the new list item tagged on the end. Another way this could be written is:

```
copy.push({id: toDoList.length + 1, task: userInput, complete: false });
```

> Make sure you pass `addTask` as props down to the `ToDoForm`.

<br>

<a name="code"></a>


## Complete To Do List code 

> Check out the complete code I used in this application [here](https://codesandbox.io/s/todo-list-hooks-ebfgw?file=/src/App.js)

[![Edit ToDo-List-Hooks](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/todo-list-hooks-ebfgw?fontsize=14&hidenavigation=1&theme=dark)

<a name="next"></a>


## What to learn next 

Congrats! You've now made a to do list using React hooks. If you found this to be fairly straightforward, play around with the code a bit and try to implement more functionality.

Here are some extra things you can do to give you some ideas: 
* Add the ability to create a due date for each task or a priority rating 
* Give the ability to sort the list by the due date or priority
* Create a backend so your To Do List can persist 
* Style application using React-Bootstrap or CSS-in-JS
* Employ the Context API by using the useContext hook instead of local state and props

If you want to get more hands-on practice, checkout Educative's course **[The Road to React: The one with Hooks](https://www.educative.io/courses/road-to-react-with-hooks)**. This course offers a deep dive React fundamentals, covering all new React concepts including Hooks. You will gain hands-on experience by building a Hacker News app!

*Happy learning!*

<br>

### Continue reading about React
* [Getting started with React and TypeScript](https://www.educative.io/blog/react-and-typescript)
* [Five best practices for React developers](https://www.educative.io/blog/best-practices-react-developer)
* [The Evolution of React: V16 and Beyond](https://www.educative.io/blog/the-evolution-of-react)