useReducer
Detalles sobre useReducer
en React
El hook useReducer
es una alternativa avanzada a useState
para manejar estados más complejos en componentes funcionales. Es útil cuando:
- Tienes un estado que involucra múltiples subvalores.
- Las actualizaciones del estado dependen de acciones específicas.
- Quieres consolidar la lógica de actualización en un solo lugar (el reducer).
Sintaxis básica de useReducer
const [state, dispatch] = useReducer(reducer, initialState);
reducer
: Es una función que determina cómo cambiar el estado. Toma dos argumentos:- El estado actual.
- La acción que describe el cambio. Devuelve el nuevo estado.
function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; } }
initialState
: El valor inicial del estado.dispatch
: Es una función que se usa para enviar acciones al reducer.
Ejemplo 1: Contador simple
import React, { useReducer } from "react";
function reducer(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 reconocida");
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Contador: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
</div>
);
}
export default Counter;
Ejemplo 2: Manejo de formularios
useReducer
puede ser útil para manejar estados de formularios complejos.
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "setName":
return { ...state, name: action.payload };
case "setEmail":
return { ...state, email: action.payload };
case "reset":
return { name: "", email: "" };
default:
throw new Error("Acción no reconocida");
}
}
function Form() {
const [state, dispatch] = useReducer(reducer, { name: "", email: "" });
const handleSubmit = (e) => {
e.preventDefault();
console.log("Datos del formulario:", state);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Nombre"
value={state.name}
onChange={(e) => dispatch({ type: "setName", payload: e.target.value })}
/>
<input
type="email"
placeholder="Email"
value={state.email}
onChange={(e) => dispatch({ type: "setEmail", payload: e.target.value })}
/>
<button type="submit">Enviar</button>
<button type="button" onClick={() => dispatch({ type: "reset" })}>
Resetear
</button>
</form>
);
}
export default Form;
Ejemplo 3: Lista de tareas (CRUD básico)
Un ejemplo más avanzado con operaciones CRUD en una lista de tareas.
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "add":
return [...state, { id: Date.now(), text: action.payload }];
case "delete":
return state.filter((task) => task.id !== action.payload);
case "edit":
return state.map((task) =>
task.id === action.payload.id
? { ...task, text: action.payload.text }
: task
);
default:
throw new Error("Acción no reconocida");
}
}
function TodoApp() {
const [tasks, dispatch] = useReducer(reducer, []);
const [input, setInput] = React.useState("");
const handleAdd = () => {
dispatch({ type: "add", payload: input });
setInput("");
};
return (
<div>
<h1>Lista de tareas</h1>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Nueva tarea"
/>
<button onClick={handleAdd}>Agregar</button>
<ul>
{tasks.map((task) => (
<li key={task.id}>
{task.text}
<button onClick={() => dispatch({ type: "delete", payload: task.id })}>
Eliminar
</button>
<button
onClick={() =>
dispatch({
type: "edit",
payload: { id: task.id, text: prompt("Edita la tarea:", task.text) },
})
}
>
Editar
</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
Ejemplo 4: Estado anidado (Carrito de compras)
Manejo de un estado más complejo, como un carrito de compras.
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "addItem":
return { ...state, items: [...state.items, action.payload] };
case "removeItem":
return {
...state,
items: state.items.filter((item) => item.id !== action.payload),
};
case "clearCart":
return { ...state, items: [] };
case "setDiscount":
return { ...state, discount: action.payload };
default:
throw new Error("Acción no reconocida");
}
}
function ShoppingCart() {
const [state, dispatch] = useReducer(reducer, { items: [], discount: 0 });
const addItem = () =>
dispatch({
type: "addItem",
payload: { id: Date.now(), name: "Producto A", price: 10 },
});
return (
<div>
<h1>Carrito de compras</h1>
<button onClick={addItem}>Agregar Producto</button>
<button onClick={() => dispatch({ type: "clearCart" })}>Vaciar Carrito</button>
<ul>
{state.items.map((item) => (
<li key={item.id}>
{item.name} - ${item.price}
<button
onClick={() => dispatch({ type: "removeItem", payload: item.id })}
>
Quitar
</button>
</li>
))}
</ul>
<p>Total: ${state.items.reduce((sum, item) => sum + item.price, 0)}</p>
</div>
);
}
export default ShoppingCart;
Consejos para usar useReducer
- Cuando usarlo:
- Úsalo en lugar de
useState
si el estado tiene lógica de actualización compleja o varios subvalores.
- Úsalo en lugar de
- Organización del código:
- Mantén el reducer separado en un archivo propio para mantener el código limpio.
- Composición:
- Combínalo con
useContext
si necesitas compartir el estado global en tu aplicación.
- Combínalo con