Estado Global: Zustand y Redux Toolkit
Context + useReducer funciona para estado moderado, pero en apps grandes con estado complejo (carrito de compras, auth, preferencias, datos cacheados) tiene problemas: cada cambio re-renderiza TODOS los consumidores del Context, y la lógica queda dispersa en múltiples reducers. Las librerías de estado global resuelven esto con re-renders selectivos y APIs más ergonómicas.
Zustand es la opción más popular por su simplicidad. Creas un "store" con create(), defines estado y acciones juntas, y los componentes se suscriben solo a las partes que necesitan — si cambia el carrito, solo se re-renderizan los componentes que usan el carrito. Redux Toolkit (RTK) es más estructura y convenciones: slices, actions, reducers inmutables con Immer. Úsalo en equipos grandes que necesitan predictabilidad estricta.
// --- ZUSTAND (minimalista, recomendado) ---
import { create } from "zustand";
const useStore = create((set, get) => ({
// Estado
items: [],
total: 0,
// Acciones (modifican el estado)
addItem: (item) => set((state) => ({
items: [...state.items, item],
total: state.total + item.precio,
})),
removeItem: (id) => set((state) => {
const item = state.items.find(i => i.id === id);
return {
items: state.items.filter(i => i.id !== id),
total: state.total - (item?.precio || 0),
};
}),
clearCart: () => set({ items: [], total: 0 }),
}));
// Componente — solo se re-renderiza si "total" cambia
function CartTotal() {
const total = useStore((s) => s.total);
return <p>Total: ${total}</p>;
}
// Componente — solo se re-renderiza si "items" cambia
function CartItems() {
const items = useStore((s) => s.items);
const removeItem = useStore((s) => s.removeItem);
return items.map(i => (
<div key={i.id}>
{i.nombre} - ${i.precio}
<button onClick={() => removeItem(i.id)}>X</button>
</div>
));
}
// --- REDUX TOOLKIT (más estructura) ---
import { configureStore, createSlice } from "@reduxjs/toolkit";
const cartSlice = createSlice({
name: "cart",
initialState: { items: [], total: 0 },
reducers: {
addItem: (state, action) => {
state.items.push(action.payload); // Immer permite "mutar"
state.total += action.payload.precio;
},
},
});
const store = configureStore({ reducer: { cart: cartSlice.reducer } });
// Componente: useSelector(s => s.cart.total), useDispatch()
Practica esta lección
Resuelve los ejercicios del curso de React en la app, gratis y con corrección automática.
Empezar a practicar