DEV Community

Cover image for Revolutionize Your React App with useReducer: Mastering State Management
Nicolas B.
Nicolas B.

Posted on • Updated on

Revolutionize Your React App with useReducer: Mastering State Management

State management is at the core of any React application, and for more complex scenarios, the useReducer hook provides a powerful way to manage state in a structured manner. In this article, we'll explore useReducer through a concrete example: creating a to-do list.


Understanding useReducer

useReducer is a React hook that allows state management using a reducer pattern. Instead of using useState to manage a component's state, useReducer is particularly suitable for handling complex states, such as lists of data.

The basic idea is to provide a reducer function that takes two arguments: the current state and the action to perform.

  • The action is typically an object that describes a change in state, such as adding a task, deleting it, or marking it as done.
  • The reducer function returns the new state based on the current state and the action.

Pratical exemple : Creating a To-Do List with useReducer

Imagine you're developing a to-do list application. Here's how you can use useReducer to manage the list of tasks:

import React, { useReducer } from 'react';

// Define a reducer function
const taskReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TASK':
      return [...state, { id: Date.now(), text: action.text, done: false }];
    case 'DELETE_TASK':
      return state.filter(task => task.id !== action.id);
    case 'TOGGLE_TASK':
      return state.map(task =>
        task.id === action.id ? { ...task, done: !task.done } : task
      );
    default:
      return state;
  }
};

function TaskList() {
  // Replace useState by useReducer with reducer and initial state
  const [tasks, dispatch] = useReducer(taskReducer, []);

  // make tasks for the reducer
  const addTask = text => {
    dispatch({ type: 'ADD_TASK', text });
  };

  const deleteTask = id => {
    dispatch({ type: 'DELETE_TASK', id });
  };

  const toggleTask = id => {
    dispatch({ type: 'TOGGLE_TASK', id });
  };

  return (
    <div>
      <h1>To-Do List</h1>
      <ul>
        {tasks.map(task => (
          <li key={task.id}>
            <span
              style={{ textDecoration: task.done ? 'line-through' : 'none' }}
            >
              {task.text}
            </span>
            <button onClick={() => toggleTask(task.id)}>
              {task.done ? 'Undo' : 'Done'}
            </button>
            <button onClick={() => deleteTask(task.id)}>Delete</button>
          </li>
        ))}
      </ul>
      <input
        type="text"
        placeholder="Add a task"
        onKeyUp={e => {
          if (e.key === 'Enter' && e.target.value) {
            addTask(e.target.value);
            e.target.value = '';
          }
        }}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example, we've created a to-do list application. We use useReducer to manage the tasks. Actions like ADD_TASK, DELETE_TASK, and TOGGLE_TASK allow us to modify the state of the task list in a structured way.


Benefits of useReducer for Task Management

useReducer brings several advantages for managing a to-do list in a React application:

  • Structure and Readability: Actions are explicitly defined with types, making the code more readable and maintainable.

  • Immutable State: useReducer encourages state immutability, which avoids side effects and unexpected errors.

  • Handling Complex States: useReducer is ideal for managing a list of data with many possible actions.

  • Separation of Concerns: By grouping actions and reduction logic in a single function, useReducer promotes the separation of concerns.

  • Potential for Future Extensions: You can easily add new actions to extend your application's functionality.


Conclusion

In conclusion, useReducer is a powerful tool for managing the state of a React application, especially when dealing with complex states and a variety of possible actions.
By incorporating it into your projects, you can create more robust and structured applications while improving code maintainability and readability.

Top comments (4)

Collapse
 
raulferreirasilva profile image
Raul Ferreira

I always had doubts about how to use it and why I should use it since there is useState and with this article it resolved some of my doubts but just to be sure lol:

Is it basically a custom and more robust useState?

Thank you for sharing your knowledge 🦤.

Collapse
 
brdnicolas profile image
Nicolas B.

Hi Raul!

Yeah it's juste a more robust useState.
It's cleaner too, you can make code more reusable with useReducer :)

Collapse
 
pimp_my_ruby profile image
Pimp My Ruby

Very interesting as always, thanks for sharing Nicolas!

Collapse
 
brdnicolas profile image
Nicolas B.

Thank's for support!