Advanced State Management with the useReducer Hook

Tame complex state logic and build more robust, predictable components with React's powerful `useReducer` hook.

count: 0

Welcome! Let's explore a more powerful way to manage state. We'll start with a familiar `useState` setup.

const [count, setCount] = useState(0);

Why Bother with useReducer?

While useState is great for simple state (like a boolean or a number), it can get messy when state logic becomes complex. Imagine managing multiple related state variables where updating one depends on another. This is where useReducer shines, offering a structured way to handle these scenarios.

The Reducer: Your State's Logic Hub

A reducer is a pure JavaScript function that takes the current state and an action object as arguments, and returns the next state. It centralizes all your state logic. The structure (state, action) => newState ensures that state transitions are predictable and easy to test.

Dispatching Actions: Triggering State Changes

You don't call the reducer directly. Instead, you use the dispatch function provided by the `useReducer` hook. You dispatch an action object (e.g., dispatch({ type: "INCREMENT" })), which tells the reducer *how* to update the state. This separates the "what to do" from the "how to do it".

The Result: Predictable State Management

By combining an initial state, a reducer function, and the `dispatch` method, `useReducer` gives you a robust pattern for managing complex state. This leads to cleaner components, as the update logic is extracted, and makes debugging easier since every state change is tied to a specific action.

Practice Zone


Interactive Test 1: Match the Concept

Match each `useReducer` concept to its correct code snippet.

Arrastra en el orden correspondiente.


Arrastra las opciones:

{ type: 'ADD_ITEM', payload: 'Milk' }
dispatch(action)
function reducer(state, action) { ... }

Completa el código:

The Logic Function______
The Command______
Sending the Command______
Unlock with Premium

Interactive Test 2: Complete the Code

Rellena los huecos en cada casilla.

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, ] = (reducer, initialState);
  return <button onClick={() => dispatch({ type: 'increment' })}>{state.count}</button>;
}
Unlock with Premium

Practice Example: Code Editor

Create a component with a reducer to manage a user's balance. It should handle 'DEPOSIT' and 'WITHDRAW' actions, each with a payload amount.

Enunciado:

* Escribe el código a continuación. Los caracteres correctos se mostrarán en verde y los incorrectos en rojo.

function BankAccount() { const initialState = { balance: 0 }; function reducer(state, action) { switch (action.type) { case 'DEPOSIT': return { balance: state.balance + action.payload }; case 'WITHDRAW': return { balance: state.balance - action.payload }; default: return state; } } const [state, dispatch] = React.useReducer(reducer, initialState); return ( <div> <h2>Balance: {state.balance}</h2> <button onClick={() => dispatch({ type: 'DEPOSIT', payload: 10 })}>Deposit 10</button> <button onClick={() => dispatch({ type: 'WITHDRAW', payload: 5 })}>Withdraw 5</button> </div> ); }

Unlock with Premium

Knowledge Check

What are the two arguments you pass directly to the useReducer hook?


Unlock with Premium

`useReducer` in Practice

`useReducer` is more than a `useState` alternative; it's a pattern for building robust and scalable components. Here's where it truly shines.


1. Complex Form State Management

For forms with many fields, interdependent validation, and submission states, `useReducer` is a lifesaver. It centralizes all form logic into one reducer, making it easy to handle complex updates in a single dispatch.

// action
dispatch({ 
  type: 'UPDATE_FIELD', 
  field: 'email', 
  value: 'test@test.com' 
});
A single, descriptive action updates the form state predictably.

2. Data Fetching Machine

Managing `loading`, `data`, and `error` states is a classic use case. A reducer can elegantly transition between these states based on actions like `FETCH_INIT`, `FETCH_SUCCESS`, and `FETCH_FAILURE`.

case 'FETCH_SUCCESS':
  return {
    ...state,
    loading: false,
    data: action.payload
  };
Loading complete, data is ready.

3. Scaling with Context API

When combined with React's Context API, `useReducer` can manage global application state, providing a lightweight alternative to libraries like Redux. You can pass the `dispatch` function down through the context, allowing any child component to trigger state updates.

<AppContext.Provider value={{ state, dispatch }}>
  <MyApp />
</AppContext.Provider>
The entire app now has access to global state and the means to update it.

Practical Takeaway: Adopt `useReducer` when your component's state logic is non-trivial. It will improve code organization, predictability, and make your components easier to test and maintain as they grow.

`useReducer` Glossary

`useReducer`
A built-in React Hook that you can use as an alternative to `useState` for managing complex component state logic.
Reducer
A pure function of the form `(state, action) => newState`. It specifies how the application's state changes in response to an action.
Action
A plain JavaScript object that represents an intention to change the state. It must have a `type` property, and can optionally contain other data in a `payload`.
Dispatch
A special function returned by the `useReducer` hook that lets you "dispatch" actions to the reducer to trigger a state update.
Payload
A conventional property name for the data you want to pass along with an action object. For example, `action.payload` might hold the amount to deposit into an account.