Gestionar el estado en aplicaciones React modernas o el state management puede ser un desafío, especialmente cuando la app crece y los datos se comparten entre muchos componentes. En este artículo, vamos a explicar cuáles son los conceptos clave de la gestión de estado y luego analizaremos tres soluciones populares: Redux, Zustand y Context API, explorando sus arquitecturas, patrones de diseño y casos de uso recomendados.
El objetivo será explicar no sólo con ejemplos de casos, si no también dar una idea de lo qué está pasando detrás de cada librería y cómo usarla correctamente.
Conceptos clave de gestión de estado
Antes de entrar en las librerías, es importante entender los elementos:
Estado (State)
El estado es toda la información que define la apariencia y el comportamiento de la aplicación en un momento dado. Por ejemplo, un contador, un tema de color o la lista de productos en un carrito
type CounterState = { count: number };
const state: CounterState = { count: 0 }; Acción (Action)
Una acción es un objeto que describe qué cambio queremos hacer en el estado. En Redux, por ejemplo:
interface IncrementAction {
type: 'counter/increment';
payload: { amount: number };
}
const incrementAction: IncrementAction = { type: 'counter/increment', payload: { amount: 5 } }; Piénsalo como un mensaje que le dice a tu app qué hacer, pero no lo hace directamente.
Reducer
Un reducer es una función pura que recibe el estado actual y una acción, y devuelve un nuevo estado. Nunca modifica el estado original.
function counterReducer(state: CounterState = { count: 0 }, action: IncrementAction): CounterState {
switch(action.type) {
case 'counter/increment':
return { count: state.count + action.payload.amount };
default:
return state;
}
} Analogía: el reducer es un juez imparcial que decide cómo cambia el estado según la acción recibida.
Store
El store es el contenedor central del estado. En Redux es único; en Zustand puedes tener varios si quieres separar responsabilidades.
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({ reducer: counterReducer }); Provider
En Context API, un Provider es un componente que proporciona el estado a todos los componentes hijos que lo necesiten.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext<string>('light');
<ThemeContext.Provider value="dark">
<App />
</ThemeContext.Provider>; Selector
Un selector es una función que elige solo la parte del estado que necesita un componente, evitando renders innecesarios.
const count = useStore((state: CounterState) => state.count);
Redux: Arquitectura Flux y Predictibilidad
Fundamentos Arquitectónicos
Redux se basa en Flux, con un flujo de datos unidireccional.
Principios clave:
- Una única fuente de verdad: un solo store.
- Estado de solo lectura: cambios solo mediante acciones.
- Reducers puros: devuelven un nuevo estado sin modificar el original.
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';
interface CounterState { count: number; }
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 } as CounterState,
reducers: {
increment: (state, action: PayloadAction<number>) => { state.count += action.payload; },
},
});
const store = configureStore({ reducer: counterSlice.reducer }); Patrones de Diseño
- Singleton: Un único store global.
- Command: Las acciones encapsulan cambios.
- Observer (Pub/Sub): Los componentes se suscriben a cambios.
- Reducer (Fold): Función pura para transformar estado.
Características Distintivas
Context API: Solución Nativa de React
Fundamentos Arquitectónicos
Context API permite compartir datos sin prop drilling. Se basa en la composición de React y proporciona Providers y Consumers.
const ThemeContext = createContext<'light' | 'dark'>('light');
function App() {
const theme = useContext(ThemeContext);
return <div className={theme}>Hola mundo</div>;
}Patrones de Diseño
- Provider-Consumer: el Provider comparte, el Consumer consume.
- Dependency Injection: inyecta estado y funciones en componentes hijos.
- No es Singleton: puedes tener varios Providers del mismo contexto.
Características Distintivas
Zustand: Minimalismo y Pragmatismo
Fundamentos Arquitectónicos
Zustand ofrece hooks para manejar el estado de manera simple y eficiente.
import create from 'zustand';
interface CounterState { count: number; increment: () => void; }
const useStore = create<CounterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })), }));
function Counter() {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
return <button onClick={increment}>{count}</button>;
} Patrones de Diseño
- Singleton opcional
- Flux simplificado
- Module pattern
- Selective subscription para evitar renders innecesarios.
Características Distintivas
Análisis Comparativo
Consideraciones de Rendimiento
- Redux: optimización con React.memo, useSelector y memorización (Reselect).
- Context API: vulnerable a re-renders excesivos; se mejora con múltiples contextos o useMemo.
- Zustand: Suscripción granular evita re-renders innecesarios de forma nativa.
Casos de Uso Recomendados
- Redux: apps complejas, auditables y con middleware sofisticado.
- Context API: apps pequeñas/medianas con estado simple.
- Zustand: apps modernas que buscan simplicidad y performance.
Patrones Arquitectónicos Transversales
Inmutabilidad
- Redux: obligatoria.
- Context API / Zustand: recomendada.
Separación de Responsabilidades
- Redux: actions + reducers .
- Context API: custom hooks.
- Zustand: stores modulares.
Testabilidad
- Redux: muy fácil (funciones puras).
- Zustand: fácil (stores independientes).
- Context API: más difícil (necesita renderizar Providers).
Conclusiones
No hay una solución perfecta. La elección depende del tamaño de la app, equipo y necesidades de rendimiento.
- Redux: robustez y ecosistema maduro para apps grandes.
- Context API: simple y nativo, perfecto para casos limitados.
- Zustand: minimalista, potente y fácil de usar.
La tendencia apunta a soluciones ligeras como Zustand, priorizando la developer experience sin sacrificar capacidad técnica, aunque Redux sigue siendo relevante en entornos empresariales.
Si te interesa estar al día en desarrollo web, arquitectura y buenas prácticas en programación, síguenos en nuestras redes sociales y Canal de YouTube.
Referencias
- Abramov, D. (2015). Redux: Predictable state container for JavaScript apps
- Facebook Inc. (2019). React Context API Documentation
- Dai, D. (2023). Zustand: Bear necessities for state management
- Gamma, E. et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software