Cómo funciona useReducer() en React y cuándo usarlo

Desde mi punto de vista, useReducer es una de esas cosas que no necesitas aprender para hacer un uso básico de React. Así lo piensan ellos también.

Básicamente, es una alternativa a useState. Realmente no tenía ni idea de esto tras casi 4 años programando en React 🤷‍♂️.

Hace poco me hizo falta finalmente y decidí escribir sobre ello para intentar entenderlo más claramente.

Vamos con un ejemplo lo más claro posible, primero para ver si lo necesitas, y después para ver cómo usar useReducer.

Un poco de contexto

Digamos que estamos controlando el estado (state) de un componente, que gestiona el aspecto de un listado.

Podríamos usar useState y olvidarnos:

import { useState } from 'react';

// en este ejemplo `view` puede ser 'grid' o 'list'
const [view, setView] = useState('grid');

// alternar entre un valor o el otro
function toggle() {
  setView((currentView) => currentView === 'grid' ? 'list' : 'grid');
}

// usamos la función `toggle` en botones, etc.

De hecho así lo hacía hasta ahora (dentro de un Context para poder acceder a esto desde toda la app).

Pero he necesitado hacer la cosa un poco más compleja para integrar nuevas posibilidades:

  • Ahora queremos que este estado dependa de múltiples factores, como vista en móvil o escritorio, valores desde localStorage, primera visita a la página o usuario registrado con preferencias sobre este ajuste, etc.

Ahí es cuando deberías usar useReducer: cuando la lógica de un estado es más completa e involucra múltiples sub-valores o el proximo valor depende del anterior

Cómo usar useRecuder()

En su uso más sencillo, el hook useReducer acepta dos valores:

  • Una función reductora (la llamaremos reducer) que definiremos. Tiene que retornar lo que quieras poner en el estado.
  • Y el valor inicial del estado.

Y devuelve dos valores. En lugar de el estado y una función para cambiarlo, como useState, useReducer devuelve:

  • El estado actual.
  • Un método dispatch, que usarás para modificar el estado.
// en lugar de:
const [view, setView] = useState('grid');

// haríamos (tras definir `reducer`):
const [view, dispatch] = useReducer(reducer, 'grid');

Veámoslo en el ejemplo anterior:

import { useReducer } from 'react';

// el valor inicial separado, ya que lo usaremos en más de un sitio
const initialView = 'grid';

// función reductora. recibe:
// - el estado actual
// - la acción que le enviamos con `dispatch`
function reducer(state, action) {
  // aquí toda la lógica para controlar el estado según la `action` recibida
  switch (action.type) {
    case: 'reset':
      // hacer algo si hemos pasado `type: 'reset'` en `dispatch`
      return initialView;
    
    case: 'change':
      // calcular algo si `type` es 'change'
    
    // etc...
    
    default:
      return state;
  }
}

// ...

// ya dentro del componente
const [view, dispatch] = useReducer(reducer, initialView);

// más tarde lo usaríamos en el componente:
return {
  <p>La vista actual es: {view}</p>

  <button onClick={() => dispatch({ type: 'reset' })}>Reset vista</button>
}

Este es un ejemplo parcial y sencillo, pero seguro que te vale para hacerte una idea.

Si el estado en vez de un valor ('grid' o 'list' en el ejemplo) fuera un objeto con varias propiedades, y necesitaras varias formas de controlarlo, como te decía al inicio, empiezas a ver la necesidad de usar useReducer.


Si la cosa va a ser bastante más compleja o vas a estar en esta situación en varias ocasiones en tu app, quizás ya te interese echar un vistazo a Redux

No hay sección de comentarios, pero me encantaría escuchar tu opinión: escríbeme en Twitter y cuéntame!