Contenido del curso
Fundamentos de Componentes y JSX
Manejo del Estado y Hooks Básicos
Estilización de Componentes
Trabajo con Datos y APIs
Componentes Avanzados y Estado Global
Introducción a TypeScript en React
Nuevas Características de React 19
useReducer para estados complejos en React
Resumen
Cuando un componente de React empieza a manejar varias acciones a la vez, useState se queda corto. Aquí entra useReducer, el hook ideal para estados complejos con múltiples transiciones, como un contador con incremento, decremento o cualquier lógica que dependa del tipo de acción.
Esta guía te muestra cómo configurar useReducer paso a paso, con un ejemplo práctico de contador en Vite y React, pensado para quienes ya dominan los estados básicos y buscan escalar su lógica.
¿Qué es useReducer y por qué usarlo en lugar de useState?
useReducer es un hook de React que centraliza la lógica de actualización de estado en una función llamada reducer. En vez de tener varios setters dispersos, defines un único lugar donde decides cómo cambia el estado según la acción recibida.
¿Cuándo conviene usar useReducer en vez de useState? Cuando tu componente maneja más de dos acciones sobre el mismo estado, valores anidados o transiciones que dependen del estado anterior. Si solo alternas un true/false, useState basta.
¿Cómo se prepara el proyecto con Vite y React?
Antes de escribir el hook, necesitas un proyecto limpio. El flujo es directo y replicable:
- Ejecuta
npm create vite@latesty selecciona React con JavaScript. - Entra a la carpeta del proyecto y corre
npm install. - Levanta el servidor con
npm run dev. - Crea la carpeta
src/componentsy dentro un archivoCounter.jsx.
Luego, en App.jsx, importa el componente Counter y elimina el contenido por defecto que trae la plantilla. Así dejas el lienzo listo para enfocarte solo en el hook.
¿Cómo se construye la función reducer paso a paso?
El reducer es una función pura que recibe dos argumentos: el estado actual y una acción. Dentro, un switch decide qué hacer según el tipo de acción.
La estructura base luce así:
jsx import { useReducer } from 'react';
const reducer = (state, action) => { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; } };
Cada case representa una transición posible. El valor 'increment' o 'decrement' lo defines tú, no es palabra reservada. El default devuelve el estado sin cambios cuando la acción no coincide con ningún caso, evitando comportamientos inesperados.
¿Qué papel cumplen state y action dentro del reducer?
El primer argumento, state, es un objeto con los valores actuales, en este ejemplo { count: 0 }. El segundo, action, es otro objeto que viaja desde el componente con al menos una propiedad type, que indica qué transición ejecutar.
Esta separación es clave: el componente no modifica el estado directamente, solo despacha una intención. La función reducer es la única responsable de calcular el nuevo estado.
¿Cómo se conecta useReducer al componente Counter?
Dentro del componente, el hook se invoca pasando el reducer y el estado inicial. Devuelve un arreglo con el estado actual y una función dispatch para enviar acciones.
jsx const [state, dispatch] = useReducer(reducer, { count: 0 });
A partir de aquí, el JSX muestra state.count y los botones disparan acciones con dispatch({ type: 'increment' }) o dispatch({ type: 'decrement' }). El render se actualiza automáticamente cuando el reducer devuelve un nuevo estado.
¿Qué hace dispatch exactamente? Dispatch envía un objeto de acción al reducer. No modifica el estado por sí mismo; solo notifica qué tipo de cambio se desea. El reducer interpreta la acción y produce el nuevo estado.
¿Cómo se ven los botones que disparan las acciones?
El componente final integra visualización y eventos en pocas líneas:
jsx return (
<div> <p>Contador: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>increment</button> <button onClick={() => dispatch({ type: 'decrement' })}>decrement</button> </div> );Al probarlo en el navegador, verás el contador iniciado en cero y dos botones que aumentan o reducen el valor. La lógica vive en un solo lugar, lo que facilita agregar más casos sin ensuciar el componente.
¿Cómo escalar el reducer a más operaciones?
El mismo patrón sirve para cualquier acción adicional. Solo agregas un case nuevo al switch y un botón que despache ese tipo:
- Multiplicar:
case 'multiply': return { count: state.count * 2 };. - Dividir:
case 'divide': return { count: state.count / 2 };. - Reiniciar:
case 'reset': return { count: 0 };.
Esta extensibilidad es la razón por la que useReducer brilla en formularios complejos, carritos de compra, filtros múltiples o cualquier componente con varias transiciones sobre el mismo estado.
El beneficio real aparece cuando dejas de pensar en setters sueltos y empiezas a razonar en términos de acciones: cada interacción del usuario se traduce en una intención clara, y el reducer decide cómo afecta al estado.
¿Qué otro caso se te ocurre para aplicar useReducer en tus proyectos? Cuéntalo en los comentarios.