January 2, 2024

Creating a todo application with React

The goal of this post is to teach you how to set up an app, not teach you react. You can learn React here.

If you haven’t already set up your React environment. Ensure you have create-react-app installed and then create a new project:

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

The structure of your app

Here is an example of how you can organize your app folder structure. Whenever I start working with a new technology to build web applications I always seek to know how to best organize my code from the beginning. It saves a ton of time and headaches later.

todo-app/
|-- src/
    |-- components/
        |-- AddTodo/
            |-- AddTodo.js
            |-- AddTodo.styles.js
        |-- TodoList/
            |-- TodoList.js
            |-- TodoList.styles.js
        |-- TodoItem/
            |-- TodoItem.js
            |-- TodoItem.styles.js
    |-- context/
        |-- TodoContext.js
    |-- hooks/
        |-- useTodos.js
    |-- utils/
        |-- utilityFunctions.js
    |-- assets/
        |-- styles/
            |-- GlobalStyle.js
        |-- images/
    |-- App.js
    |-- index.js

  • App.js – The main component that holds everything together.
  • TodoList.js – Displays the list of to-do items.
  • TodoItem.js – Represents a single to-do item.
  • AddTodo.js – A form to add new to-do items.

We are going to create the last 3.

Creating the TodoList and TodoItem Components

Create a Todolist.js file, use the directory structure example to guide you on where to save it.

function TodoItem({ todo, toggleTodo }) {
  return (
    <li
      style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
      onClick={() => toggleTodo(todo.id)}
    >
      {todo.text}
    </li>
  );
}

export default TodoItem;

Here we are declaring our TodoItem component and it takes two props. The todo object itself and a handler to toggle the todo’s status from pending to completed.

In the same manner create TodoList.js and paste the following code.

import TodoItem from './TodoItem';

function TodoList({ todos, toggleTodo }) {
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} toggleTodo={toggleTodo} />
      ))}
    </ul>
  );
}

export default TodoList;

Like TodoItem, it takes two properties, a list of todos and a toggleTodo method that we are then passing down to each item. There are more advance and better ways of handling the state of the todo’s but our goal here is to get your started.

Now that our todo components are ready, lets move on to managing the state of the items.

Managing State in App.js

In App.js we are going to implement toggleTodos as well as an add method to add a todo that. We will take care of fully implementing it later.

import React, { useState } from 'react';
import TodoList from './TodoList';
import AddTodo from './AddTodo'; // We'll create this next

function App() {
  const [todos, setTodos] = useState([]);

  const toggleTodo = id => {
    setTodos(
      todos.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  const addTodo = text => {
    setTodos([...todos, { id: Date.now(), text: text, completed: false }]);
  };

  return (
    <div>
      <AddTodo addTodo={addTodo} />
      <TodoList todos={todos} toggleTodo={toggleTodo} />
    </div>
  );
}

export default App;

There are several things going on here. First we use the useState hook manage our todos list state. useState adds a state variable that can be used to update and re-render our application anytime the items are updated such as marking one of the todos completed.

We also implemented toggleTodos, it takes the ID of the todo and toggles the completed property by looping through our list until it finds the todo with the ID.

We are using our TodoList component and passing it a list of todos and toggleTodos.

We also implemented addTodo which we will handle next.

Adding a new Todo item

For adding an item to our list of todos, we are going to create a new component. It you look at App.js we actually already used it. This component will render a form collecting the name of the todo item and call the addTodo method passed to it from App.js. In this way, our App component will take care of managing this simple state. Lets implement it.

import React, { useState } from 'react';

function AddTodo({ addTodo }) {
  const [value, setValue] = useState('');

  const handleSubmit = e => {
    e.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={value}
        onChange={e => setValue(e.target.value)}
        placeholder="Add a new todo"
      />
      <button type="submit">Add Todo</button>
    </form>
  );
}

export default AddTodo;

Our AddTodo component, renders a form and a button. It also validates that the input field in the form actually has a value. We use useState again to manage the state. We also attached to the onChange event of the input field to update our value state variable every time the value of the input field is updated. When the user clicks the Add Todo button, we pass value to addTodo and it will take care of adding the item to our list. We also set value to an empty string leaving the form ready for the user to add another todo.

Congratulations! You’ve just built a basic to-do app with React.