useReducer es un hook de React que te permite manejar estados complejos en componentes funcionales, proporcionando una alternativa al uso de useState.
Es particularmente útil cuando tienes múltiples actualizaciones de estado o lógica más elaborada.
useReducer?Es un hook que implementa el patrón de los reducers, similar al usado en Redux.
Se basa en una función reducer que toma el estado actual y una acción como argumentos, y retorna un nuevo estado.
const [state, dispatch] = useReducer(reducer, initialState); reducer: Función pura que actualiza el estado.initialState: Valor inicial del estado.state: Estado actual.dispatch(action): Función que ejecuta una acción para actualizar el estado.Un contador con acciones para incrementar, decrementar y reiniciar.
import { useReducer } from "react";
const counterReducer = (state, action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
case "reset":
return { count: 0 };
default:
throw new Error("Acción no soportada");
}
};
const Counter = () => {
const initialState = { count: 0 };
const [state, dispatch] = useReducer(counterReducer, initialState);
return (
<div>
<h1>Contador: {state.count}</h1>
<button onClick={() => dispatch({ type: "increment" })}>Incrementar</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrementar</button>
<button onClick={() => dispatch({ type: "reset" })}>Reiniciar</button>
</div>
);
};
export default Counter; count inicializada en 0.Controla el estado de múltiples campos en un formulario.
import { useReducer } from "react";
const formReducer = (state, action) => {
switch (action.type) {
case "update_field":
return { ...state, [action.field]: action.value };
case "reset":
return { name: "", email: "" };
default:
throw new Error("Acción no soportada");
}
};
const Form = () => {
const initialState = { name: "", email: "" };
const [state, dispatch] = useReducer(formReducer, initialState);
const handleChange = (e) => {
const { name, value } = e.target;
dispatch({ type: "update_field", field: name, value });
};
const handleReset = () => {
dispatch({ type: "reset" });
};
return (
<div>
<h1>Formulario</h1>
<input
type="text"
name="name"
placeholder="Nombre"
value={state.name}
onChange={handleChange}
/>
<input
type="email"
name="email"
placeholder="Correo"
value={state.email}
onChange={handleChange}
/>
<button onClick={handleReset}>Reiniciar</button>
<p>Nombre: {state.name}</p>
<p>Correo: {state.email}</p>
</div>
);
};
export default Form; dispatch para actualizar un campo específico.Maneja la lógica de agregar,eliminar y vaciar un carrito.
import React, { useReducer } from "react";
const cartReducer = (state, action) => {
switch (action.type) {
case "add_item":
return [...state, action.item];
case "remove_item":
return state.filter((item) => item.id !== action.id);
case "clear_cart":
return [];
default:
throw new Error("Acción no soportada");
}
};
const ShoppingCart = () => {
const initialState = [];
const [cart, dispatch] = useReducer(cartReducer, initialState);
const addItem = (item) => {
dispatch({ type: "add_item", item });
};
const removeItem = (id) => {
dispatch({ type: "remove_item", id });
};
const clearCart = () => {
dispatch({ type: "clear_cart" });
};
return (
<div>
<h1>Carrito de Compras</h1>
<button onClick={() => addItem({ id: 1, name: "Producto A" })}>
Agregar Producto A
</button>
<button onClick={() => addItem({ id: 2, name: "Producto B" })}>
Agregar Producto B
</button>
<button onClick={clearCart}>Vaciar Carrito</button>
<ul>
{cart.map((item) => (
<li key={item.id}>
{item.name} <button onClick={() => removeItem(item.id)}>Eliminar</button>
</li>
))}
</ul>
</div>
);
};
export default ShoppingCart; dispatch con las acciones correspondientes.Este ejemplo muestra un caso más avanzado de useReducer, manejando un array de tareas (todos) junto con estados adicionales como loading y error.
Es ideal para entender cómo trabajar con múltiples acciones y estados más complejos, incluyendo escenarios asincrónicos y actualizaciones condicionales.
Además, este ejemplo refleja cómo mantener la lógica organizada y reutilizable en aplicaciones reales.
const todoInitialState = {
todos: [],
loading: false,
error: null
};
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
case 'REMOVE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
case 'SET_LOADING':
return {
...state,
loading: action.payload
};
case 'SET_ERROR':
return {
...state,
error: action.payload
};
default:
return state;
}
}
function TodoList() {
const [state, dispatch] = useReducer(todoReducer, todoInitialState);
const addTodo = (text) => {
dispatch({
type: 'ADD_TODO',
payload: { id: Date.now(), text, completed: false }
});
};
return (
<div>
{state.loading && <p>Loading...</p>}
{state.error && <p>Error: {state.error}</p>}
<ul>
{state.todos.map(todo => (
<li
key={todo.id}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
{todo.text}
<button
onClick={() => dispatch({
type: 'TOGGLE_TODO',
payload: todo.id
})}
>
Toggle
</button>
<button
onClick={() => dispatch({
type: 'REMOVE_TODO',
payload: todo.id
})}
>
Delete
</button>
</li>
))}
</ul>
</div>
);
} useReducer es una herramienta poderosa para manejar estados complejos en React.
Dominarlo te permitirá escribir código más limpio, organizado y fácil de escalar.
Si aún tienes dudas te recomiendo mirar estos otros ejemplos prácticos: