Sign in
Log inSign up
Creating A Todo App With React Hooks

Creating A Todo App With React Hooks

Yomi Onisade's photo
Yomi Onisade
·Apr 26, 2020

I started learning React in late January and i have found it fun and exciting. Due to advice from different experienced developers, documenting and writing a simple article about what you have learnt helps retain the knowledge and is vital for your career. So i decided to give it a try.

We start by creating a new react app by running npx create-react-app todo-app in the command prompt or Git Bash, cd into the todo-app and run npm start

npx create-react-app todo-app
cd todo-app
npm start

This will generate the boilerplate that will be required to start our react app.

We open the app folder in our text editor and navigate to the App.js and edit the react-starter text-markup to print Hello World

import React  from 'react';

function App() { 
 return (
  <div>Hello World</div>
)
}

Hello.jpg

We set up a todo state in the functional component using useState, print it out, add a checkbox(to mark completed) and delete button to each todo item. We also set up a ternary operator to check if there is no Todos and render Todo Empty(this will come in handy when we start deleting todos) if no todo.

import React, { useState }  from 'react';

function App() { 
 const [Todos, setTodos] = useState([
      {id: 1, title: 'Todo', checked: false},
      {id: 2, title: 'Todo 2', checked: false},
      {id: 3, title: 'Todo 3', checked: false}
    ])
 return (
    <div 
    style={{ textAlign: 'center'}} >
        <h1>Todo App</h1>
        {Todos.length <= 0 ? <h3> Todo Empty...</h3> : Todos.map(todo => (
          <div key={todo.id}>
            <h3>{todo.title}</h3>
            <input
              type="checkbox"
              onChange={() =>handleChange(todo.id)}
              checked={todo.checked}
            />
            <button
            onClick={() => handleDelete(todo.id)}
            >Delete</button>
          </div>
        ))}
    </div>
  );
}

Todos.png

With the current set-up the checkbox will never be checked because its value is set to false by default(todo.checked === false). We set up a function handleChange to handle this check button

  const handleChange = (id) => {
      let newTodos = [...Todos]
      let todo = newTodos.find(t => t.id === id)
      todo.checked = !todo.checked
      setTodos(newTodos)
    }

This allow us to toggle the checkbox but we need to add functionality to the title to show that the todo has been checked/ completed, so we define a const marked and function changeStyle to change the style of the h3 title element color to red(I don't want to set up a seperate css sheet) on checking the checkbox

const marked = {
    color: 'red',
    textDecoration: 'line-through',
    display: 'inline'
}

  const changeStyle = (todo) => {
      return todo.checked ? marked : { display: 'inline'}
    }

The h3 title looks like this

<h3 style={changeStyle(todo)}>{todo.title}</h3>

MarkTodo.png To add an input field that adds new Todo to the existing ones, first we create a JSX markup directly below the Todo App header

      <input 
          name="todo"
          type="text"
          onChange={handleInput} 
          value={todo}
        />
        <button onClick={addTodo}>Add Todo</button>

addInput.png

Now we need three set-ups:

  1. We set up the useState for todo in order to manage/communicate with input change
  2. We set up the handleInput functionality in order to set the state on input change
  3. We set up the addTodo functionality in order to add todo to the todo list

1. We set up the useState for todo in order to manage/communicate with input change

const [todo, settodo] = useState('')

2. We set up the handleInput functionality in order to set the state on input change

   const handleInput =(e) => {
      settodo(e.target.value)
    }

3. We set up the addTodo functionality in order to add todo to the todo list

Note that we already added a simple valid check for empty string on the Add Todo button{ disabled={!todo} }, so that should do for simple validation. We also set the todo to empty string to clear the input once we add todo.

   const addTodo = () => {
      const newTodo = {
        id: Date.now() + todo,
        title: todo,
        checked: false
      }
      const newTodos = [...Todos, newTodo]
      setTodos(newTodos)
      settodo('')
    }

We should be able to add new todo

addTodo.png

Now to set up the functionality for deleting todo

    const deleteTodo = (id) => {
      const newTodos = [...Todos]
      const filteredTodos = newTodos.filter(t => t.id !== id)
      setTodos(filteredTodos)
      saveToStorage(newTodos)
    }

Now we should be able to delete any of the todo list. Lets delete the first one.

deleteTodo.png

Now to add one more functionality. The todos are static and would rerender the three todos we started with once we refresh or reload the page. To make it dynamic we set up another react hooks functionality known as useEffect by setting and getting information from the browser local storage.

We define the saveToStorage and getFromStorage functionality

  const getFromStorage = (item) => {
      const fromStore = JSON.parse(localStorage.getItem(item))
      return !fromStore || typeof fromStore === "undefined" || fromStore.length <= 0 ? [] : fromStore
    }

   const saveToStorage = (todos) => {
      localStorage.setItem('todos', JSON.stringify(todos))
    }

We add the saveToStorage functionality to the handleChange, addTodo and deleteTodo functions

 const addTodo = () => {
      const newTodo = {
        id: Date.now() + todo,
        title: todo,
        checked: false
      }
      const newTodos = [...Todos, newTodo]
      setTodos(newTodos)
      settodo('')
      saveToStorage(newTodos)
    }

    const deleteTodo = (id) => {
      const newTodos = [...Todos]
      const filteredTodos = newTodos.filter(t => t.id !== id)
      setTodos(filteredTodos)
      saveToStorage(newTodos)
    }

Now to add the useEffect functionality

import React, {useState, useEffect } from 'react';

  useEffect(() => {
      const todosFromStore = getFromStorage('todos') 
      setTodos(todosFromStore)
    }, [])

Voila, our todos are now dynamic and we can reset the static one to an empty array.

  const [Todos, setTodos] = useState([])

Load From Storage.png

Thanks for reading. (NB: Sorry for my poor write-up, I hope to write better and more comprehensive in future)