useReducer y Estado Complejo
useReducer es como un control remoto con botones específicos para tu televisor (el estado). Cada botón (acción) hace algo diferente: ENCENDER, CAMBIAR_CANAL, SUBIR_VOLUMEN. El reducer es la lógica interna del TV que decide qué hacer con cada botón. Con useState tendrías setEncendido, setCanal, setVolumen, y la lógica dispersa en múltiples handlers. Con useReducer tienes dispatch({ type: "CAMBIAR_CANAL", canal: 5 }) y toda la lógica centralizada en un solo lugar predecible.
Usa useReducer cuando: tienes 3 o más estados relacionados que se actualizan juntos, el nuevo estado depende del anterior de formas complejas, o hay muchas operaciones posibles sobre el mismo estado (como un carrito de compras con agregar, quitar, aumentar cantidad, vaciar). El reducer es una función pura: misma entrada → misma salida, sin efectos secundarios. Esto lo hace muy fácil de testear de forma aislada con expect(reducer(estado, accion)).toEqual(estadoEsperado).
import { useReducer } from "react";
// Reducer - función pura que calcula el nuevo estado
function carritoReducer(estado, accion) {
switch (accion.type) {
case "AGREGAR":
const existente = estado.items.find(i => i.id === accion.producto.id);
if (existente) {
return {
...estado,
items: estado.items.map(i =>
i.id === accion.producto.id
? { ...i, cantidad: i.cantidad + 1 }
: i
)
};
}
return {
...estado,
items: [...estado.items, { ...accion.producto, cantidad: 1 }]
};
case "ELIMINAR":
return {
...estado,
items: estado.items.filter(i => i.id !== accion.id)
};
case "LIMPIAR":
return { items: [], total: 0 };
default:
return estado;
}
}
const estadoInicial = { items: [], total: 0 };
function Carrito() {
const [estado, dispatch] = useReducer(carritoReducer, estadoInicial);
const agregar = (producto) =>
dispatch({ type: "AGREGAR", producto });
const eliminar = (id) =>
dispatch({ type: "ELIMINAR", id });
const total = estado.items.reduce(
(acc, i) => acc + i.precio * i.cantidad, 0
);
return (
<div>
<button onClick={() => agregar({ id: 1, nombre: "Laptop", precio: 999 })}>
+ Laptop
</button>
<p>Total: ${total.toFixed(2)}</p>
<ul>
{estado.items.map(i => (
<li key={i.id}>
{i.nombre} x{i.cantidad}
<button onClick={() => eliminar(i.id)}>x</button>
</li>
))}
</ul>
<button onClick={() => dispatch({ type: "LIMPIAR" })}>Vaciar</button>
</div>
);
}
Practica esta lección
Resuelve los ejercicios del curso de React en la app, gratis y con corrección automática.
Empezar a practicar