Search⌘ K

Combining the Puzzle Pieces

Explore how to manage state in React by building a functional to-do list app using Redux. Learn to connect components to the store, handle actions and reducers, and use the Provider. Understand how to visualize state changes with Redux DevTools for effective debugging.

To-do list app example

We now know how to use the connect() method and why we need a Provider. Let’s look at a more detailed and complete example that outlines a totally functional to-do list app. It allows us to add new to-do items, mark them as complete or not, and allows us to remove them if we want:

import React, { useState } from 'react';
import { connect } from 'react-redux';
import { addTodo, removeTodo, changeStatus } from './store/todos/actions';

const TodoList = (props) => {
    const [todoText, setTodoText] = useState('');

    return (
        <div>
            <p>{props.todos.length} Todos.</p>
            <ul>
                {props.todos.map((todo) => (
                    <li key={todo.id}>
                        <button
                            type="button"
                            onClick={() => {
                                props.removeTodo(todo.id);
                            }}
                        >
                            remove
                        </button>
                        <label
                            style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
                        >
                            <input
                                type="checkbox"
                                name={todo.id}
                                checked={Boolean(todo.done)}
                                onChange={(e) => {
                                    const { name, checked } = e.target;
                                    props.changeStatus(name, checked);
                                }}
                            />
                            {todo.text}
                        </label>
                    </li>
                ))}
            </ul>
            <input onChange={(e) => setTodoText(e.target.value)} value={todoText} />
            <button
                type="button"
                onClick={() => {
                    props.addTodo(todoText);
                    setTodoText('');
                }}
            >
                add
            </button>
        </div>
    );
};

const mapStateToProps = (state) => ({
    todos: state.todos,
});

const mapDispatchToProps = {
    addTodo,
    removeTodo,
    changeStatus,
};

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(TodoList);

We’ve defined the todosReducer as well as addTodo, removeTodo, and changeStatus actions. To aid readability, both reducers and actions have been extracted into their own files within the ./store/todos directory.

Note: There’s always heated debate about how folders and files should be structured within an application. Some find a separation by the domain (for example, todos, user, repositories, etc.) and according to type ( ...