SKJ Blog

Menu

.
back to js articles

Make a Todo List App in ReactJS

Shallom Kyle Jacinto

April 21st 2021 5 minute read

In this article we will be making a todo list app in react js es6 functions, improve your react skills by building project.

Prerequisites

A node.js installed in your computer.

Knowledge of Javascript array methods since we'll store our todos in an array.

A basic knowledge of ReactJS.

Setup

First open your terminal or cmd then cd to your project path then run ...

npx create-react-app todo-app

Some linux users require root so run ...

sudo npx create-react-app todo-app

After installation run cd todo-app and npm run start, then open your code editor.

Folder Structure

- node_modules 
- public 
- src
   \ components
      \ TodoForm.js
      \ TodoList.js
      \ TodoUpdateForm.js
   - App.js
   - Index.js
- .gitignore
- package.json 
- README.md

In our src folder we deleted App.css and index.css and logo.svg since we will not doing any styling in our app, we deleted App.test.js, reportWebVitals.js and setUpTests.js cause we will not doing a test run in our app.

Inside in our App.js

function App() {
  return (
    <div>
    </div>
  );
}

export default App;

Inside in our index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

Todo Form Component

We create a folder name components then inside inside in our component folder, we create TodoForm.js which is for creating a todo.

TodoForm.js

const TodoForm = () => {
  return (
    <div>
      <h2>Todo Form</h2>
    </div>
  )
}
export default TodoForm

In order to work, we must import TodoForm.js in our App.js.

import TodoForm from './components/TodoForm'
function App() {
  return (
    <div>
      <h2>Todo App</h2>
      <TodoForm />
    </div>
  );
}
export default App;

Inside our TodoForm.js, in return we add form to handle our todo form for creating a new todo.

return (
  <div>
    <h2>Todo Form</h2>
    <form>
      <input 
        type="text" 
        placeholder="Todo text..." 
      />
      <button type="submit">Add todo</button> 
    </form>
  </div>
)

Create Todo

In our form tag, we add a onSubmit which if a button submitted then this onSubmit will handle the event and we pass a dynamic function createTodo for creating our todo.

<form onSubmit={createTodo}> ... </form>

And Inside in our TodoForm function we add a function name createTodo.

const TodoForm = () => {
  const createTodo = () => {
    ...
  }
  return (
    ...
  )
}

Todos State

For creating our todo, we create a todo state which it makes our todo as a reactive because when you just make a todo like let todo = {}, our todo will not re- render and not reactive so we use a hook useState to make our todo reactive and will re-render.

import { useState } from 'react'
const TodoForm = () => {
  const [todo, setTodo] = useState({})
  ...
}

We import useState in order work our reactive todo and for initial value we add {} since our todo is an object because there is two value, the id and the text.

In our input tag, we add a onChange which will handle an event if there is changing value in our input then we pass a dynamic anonymous function and we set the todo value from input value.

<input
  ...
  onchange={(e) => setTodo({ id: Date.now(), text: e.target.value })}
/> 

In our createTodo function we add a preventDefault() to prevent our page refresh and we console log our todo.

const createTodo = (e) => {
  e.preventDefault()
  console.log(todo)
}

Our console

Great! Now to display our todo we create a new state which is todos, all our todo will be stored in our todos state.

const [todos, setTodos] = useState({})

Now there is an issue when pushing a new array in our array, open this link for explanation so in order to push we use concat to concatenate our object in todos.

setTodos(todos => todos.concat(todo))
console.log(todos)

Try to add todo and you'll see that our todo has been save on todos state.

Display Todos

To display our todos, we create a new component name TodoList.js which to handle all todos and display it.

const TodoList => () => {
  return (
    <div>
       <h3>Todo List</h3>
    </div>
  )
}
export default TodoList

Then inside in our TodoForm.js, we import TodoList.js and display it.

import TodoList from './TodoList'
const TodoForm => () => {
  return (
    ...
    <TodoList />
  )
}
export default TodoForm

To display the todos, we pass the todos as a props in TodoList tag.

<TodoList todos={todos} />

Then in our TodoForm.js, we deconstruct the prop todos and loop it with map since we cannot loop an array with forEach.

const TodoList = ({ todos }) => {
  return (
    <div>
      <h3>Todo List</h3>
      {todos.map((todo, index)  => (
        <div key={todo.id}>
           <p>{todo.text}</p>
           <button>Delete</button>
           <button>Update</button>
        </div>
      ))}
    </div>
  )
}
export default TodoList

Try to add something and it will display the todos, Great!

Update Todo

We create a function name updateTodo, then we pass this function as a props to use it in TodoList.

const updateTodo = () => {
   ...
}
<TodoList updateTodo={updateTodo} />
<button onClick={() => updateTodo(todo.id)}>Update </button>

We pass the id in update cause we will be using it to update the todo according to its id.

We create a new component name TodoUpdateForm.js which will handle the update.

const TodoUpdateForm = () => {
  return (
    <div>
      <h3>Todo Update</h3>
      <form>
        <input 
          type="text" 
        />
        <button type="submit">Save Update</button> 
      </form>
    </div>
  )
}
export default TodoUpdateForm

We create a new state isUpdate which if a update button was pressed then we set the isUpdate to true to make our todo display.

const [isUpdate, setIsUpdate] = useState(false)

The <TodoUpdateForm /> will only be show if isUpdate is true.

return(
  <div>
    ...
    {isUpdate && <TodoUpdateForm />}
  </div>
)

Inside in our updateTodo function, we add a setIsUpdate(true) to display our update form.

We create a variable name tempTodos which is temporary todos since we cant directly change our state so we copy the todos by using a spread.

const updateTodo = (id) => {
  setIsUpdate(true)
  let tempTodos = [...todos]
}

We create a new state updateTodoText which our new todo updated text and also for updateTodoId.

const [updateTodoText, setUpdateTodoText] = useState("")
const [updateTodoId, setUpdateTodoId] = useState("")

In our updateTodo function we loop the todos and we set the value of the update form input element and we set the updateTodoId.

tempTodos.forEach(todo => {
  if(todo.id === id) {
    setUpdateTodoText(todo.text)
    setUpdateTodoId(todo.id)
  }
})

we pass the updateTodoText as a props in TodoUpdateForm and also the setUpdateTodoText

<TodoUpdateForm updateTodoText={updateTodoText} setUpdateTodoText={setUpdateTodoText} />

In our TodoUpdateForm we set the input value to updateTodoText which to display the value from array and we set the updateText by adding a onChange to set the new value.

<input 
   type="text" 
   value={updateTodoText}
   onChange={(e) => setUpdateTodoText(e.target.value)}
/>

Save Update Todo

To save our update, we create new function saveUpdate , we pass this function as a prop to TodoUpdateForm and inside in our function we copy the todos and loop it and we set the todo.text from updateTodoText, we set the todos state from updated todos and we set the update form to false to hide it.

<TodoUpdateForm updateTodoText={updateTodoText} setUpdateTodoText={setUpdateTodoText} saveUpdate={saveUpdate} />

TodoUpdateForm.js

const TodoUpdateForm = ({ saveUpdate, updateTodoText, setUpdateTodoText }) => {
  return (
    ...
      <form onSubmit={saveUpdate}> ... </form>
  )
}

export default TodoUpdateForm
const saveUpdate = (e) => {
   e.preventDefault()
   let tempTodos = [...todos]
   tempTodos.forEach(todo => {
     if(todo.id === updateTodoId) {
        todo.text = updateTodoText
      }
    })
   setTodos(tempTodos)
   setIsUpdate(false)
}

Delete Todo

To delete our todo we create a function deleteTodo, we pass this function as a prop to TodoList.

<TodoList ... deleteTodo={deleteTodo} />

And we pass the index of the array to deleteTodo.

<button onClick={() => deleteTodo(index)}>Delete</button>

Then in our deleteTodo function, we copy the todos, we delete the todo according to its id by using splice method and we set the todos`.

const deleteTodo = (index) => {
  let tempTodos = [...todos]
  tempTodos.splice(index,1)
  setTodos(tempTodos)
}

Thats it! Now you already know how to CRUD in ReactJS.

Source Code

For source code, open this github repo


js reactjs