Cuándo usar Context vs librerías de estado

Lectura
20 min~8 min lectura
CONCEPTO CLAVE: La gestión de estado en React es una de las decisiones más importantes al diseñar una aplicación. Elegir entre Context API y librerías dedicadas como Redux, Zustand o MobX puede determinar la escalabilidad y mantenibilidad de tu proyecto a largo plazo. No existe una solución universal; la mejor elección depende del contexto específico de tu aplicación.

Introducción al Problema de Estado en React

Cuando comenzamos con React, el estado local con useState es suficiente para la mayoría de los casos. Pero conforme nuestra aplicación crece, necesitamos compartir datos entre componentes que no tienen relación directa en el árbol de componentes. Aquí es donde surge la pregunta: ¿debemos usar Context API o una librería de gestión de estado?

📌 Antes de profundizar, recordemos que tanto Context API como las librerías de estado resuelven el mismo problema fundamental: hacer que datos estén disponibles en cualquier parte de la aplicación sin necesidad de pasar props manualmente a través de cada nivel del árbol de componentes.

Context API: El Enfoque Nativo de React

Context API es la solución integrada en React para compartir datos que pueden considerarse "globales" para un árbol de componentes. Fue introducido en React 16.3 y refinado en versiones posteriores.

¿Cuándo Context API es la Mejor Elección?

💡 Context API brilla especialmente cuando necesitas compartir datos que cambian con poca frecuencia y que no requieren lógica compleja de gestión.
  1. Temas y preferencias de usuario: El tema oscuro/claro de tu aplicación es un caso perfecto. No cambia frecuentemente y necesitas acceso desde cualquier componente.
  2. Datos de autenticación: El usuario logueado, sus permisos y tokens de sesión son ideales para Context.
  3. Configuración regional: El idioma seleccionado o la moneda preferida funcionan muy bien con Context.
  4. Datos que fluyen desde el servidor: Información del usuario que se carga una vez y se actualiza ocasionalmente.

Ejemplo Práctico de Context

// ThemeContext.jsx
import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export const useTheme = () => useContext(ThemeContext);

// Uso en cualquier componente
function Header() {
  const { theme, toggleTheme } = useTheme();
  return (
    <header className={theme}>
      <button onClick={toggleTheme}>Cambiar tema</button>
    </header>
  );
}

Limitaciones de Context API

⚠️ Context tiene un problema importante de rendimiento: cualquier cambio en el contexto provoca un re-render en TODOS los componentes que consumen ese contexto, incluso si solo les interesa una pequeña parte del estado.

Esto significa que si tienes un contexto con muchos datos que cambian frecuentemente, podrías experimentar problemas de rendimiento significativos. Context no está diseñado para ser un sistema de gestión de estado completo; es más bien un mecanismo de "inyección de dependencias" simplificado.

Ver más sobre la limitación de rendimiento de Context

Imaginemos que tienes un contexto de usuario con múltiples propiedades: nombre, avatar, preferencias, historial, etc. Si solo necesitas el nombre en un componente y la preferencia de idioma cambia, ese componente se re-renderizará innecesariamente. Las librerías de estado como Redux o Zustand permiten un control más granular de qué componentes se actualizan.

Librerías de Gestión de Estado: El Enfoque Especializado

Las librerías de gestión de estado nacieron para resolver problemas que Context API no aborda completamente. Las más populares incluyen Redux, Zustand, Jotai, MobX y Recoil.

¿Cuándo Usar una Librería de Estado?

💡 Las librerías de estado son la elección correcta cuando necesitas gestión compleja, optimizaciones de rendimiento finas, o cuando trabajas en equipos grandes con múltiples desarrolladores.
  1. Aplicaciones con estado complejo y entrelazado: Cuando múltiples partes de tu UI dependen de los mismos datos de formas diferentes.
  2. Datos que cambian frecuentemente: Un carrito de compras en tiempo real, un editor colaborativo, o datos de un juego.
  3. Necesidad de herramientas de desarrollo avanzadas: Redux DevTools permite viajar en el tiempo, ver el historial de acciones, y depurar fácilmente.
  4. Middleware y efectos secundarios: Cuando necesitas logging, persistencia automática, o sincronización con APIs de forma centralizada.
  5. Optimización de rendimiento: Selectores en Redux o primitivas atómicas en Zustand permiten renderizar solo lo necesario.

Ejemplo con Zustand (más moderno y simple)

// useStore.js
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set, get) => ({
      cart: [],
      addToCart: (product) => set((state) => ({
        cart: [...state.cart, { ...product, id: Date.now() }]
      })),
      removeFromCart: (id) => set((state) => ({
        cart: state.cart.filter(item => item.id !== id)
      })),
      total: () => get().cart.reduce((sum, item) => sum + item.price, 0),
    }),
    { name: 'shopping-cart' }
  )
);

// Uso optimizado - solo se re-renderiza si cart cambia
function CartIcon() {
  const cart = useStore((state) => state.cart);
  const itemCount = cart.length;
  return <div>🛒 {itemCount}</div>;
}

// Este componente solo se re-renderiza si el total cambia
function CartTotal() {
  const total = useStore((state) => state.total());
  return <div>Total: ${total}</div>;
}

Ejemplo con Redux Toolkit (el estándar de la industria)

// store/cartSlice.js
import { createSlice } from '@reduxjs/toolkit';

const cartSlice = createSlice({
  name: 'cart',
  initialState: {
    items: [],
    status: 'idle'
  },
  reducers: {
    addItem: (state, action) => {
      state.items.push(action.payload);
    },
    removeItem: (state, action) => {
      state.items = state.items.filter(item => item.id !== action.payload);
    },
    clearCart: (state) => {
      state.items = [];
    }
  }
});

export const { addItem, removeItem, clearCart } = cartSlice.actions;
export default cartSlice.reducer;

// Componente React
import { useSelector, useDispatch } from 'react-redux';
import { addItem } from '../store/cartSlice';

function ProductCard({ product }) {
  const dispatch = useDispatch();
  const cartItems = useSelector(state => state.cart.items);
  const isInCart = cartItems.some(item => item.id === product.id);
  
  return (
    <div>
      <h3>{product.name}</h3>
      <button 
        onClick={() => dispatch(addItem(product))}
        disabled={isInCart}
      >
        {isInCart ? 'En el carrito' : 'Añadir'}
      </button>
    </div>
  );
}

Comparativa Directa: Context vs Librerías

AspectoContext APIRedux/Zustand
Curva de aprendizajeBaja (viene con React)Media-Alta (requiere aprender conceptos)
Tamaño del bundleMínimo (incluido en React)Variable (Redux ~7KB, Zustand ~1KB)
DevToolsBásicasAvanzadas (time-travel debugging)
Re-rendersTodos los consumidoresSolo componentes suscritos
MiddlewareNo disponibleSoporte completo
PersistenciaManualPlugins integrados
Mejor paraDatos estáticos/infrecuentesEstado complejo/frecuente
📌 La tabla anterior es una guía general. En la práctica, muchas aplicaciones híbridas usan Context para datos simples (tema, idioma) y librerías de estado para datos complejos (carrito, datos de usuario con lógica de negocio).

Guía de Decisión Práctica

Para ayudarte a tomar la mejor decisión para tu proyecto, aquí tienes un checklist basado en preguntas:

  1. ¿Con qué frecuencia cambia el estado? Si es menos de una vez por interacción del usuario, Context está bien. Si cambia constantemente (datos en tiempo real, animaciones), usa librería.
  2. ¿Cuántos componentes necesitan acceder al estado? Pocos: props son suficientes. Medios: Context. Muchos con diferentes necesidades: librería.
  3. ¿Necesitas lógica de negocio compleja? Acciones asíncronas, validaciones, efectos secundarios: librería.
  4. ¿Cuál es el tamaño de tu equipo? Equipos grandes se benefician de la estructura y herramientas de Redux. Proyectos personales o pequeños: Context puede ser suficiente.
  5. ¿Tienes problemas de rendimiento? Si ya experimentas re-renders innecesarios, una librería con selectores optimizados es necesaria.
⚠️ Error común: usar Redux o cualquier librería de estado "por si acaso" cuando tu aplicación es pequeña. Esto añade complejidad innecesaria. Comienza simple y escala cuando sea necesario.

Patrón Híbrido: Lo Mejor de Ambos Mundos

En aplicaciones reales, el enfoque híbrido es frecuentemente el más efectivo:

Usa Context para lo que realmente es contexto: el tema actual, el idioma seleccionado, el usuario autenticado. Usa una librería de estado para datos que representan el "estado de la aplicación": el carrito de compras, la lista de tareas, los filtros activos, los datos cargados del servidor.
// Ejemplo de estructura híbrida
// contexts/ThemeContext.jsx - Context para tema (cambia raramente)
// contexts/AuthContext.jsx - Context para auth (cambia poco)
// store/useStore.js - Zustand para estado de negocio (cambia frecuentemente)

// App.jsx
function App() {
  return (
    <ThemeProvider>
      <AuthProvider>
        <StoreProvider>
          <Router />
        </StoreProvider>
      </AuthProvider>
    </ThemeProvider>
  );
}

Conclusión y Recomendaciones

La elección entre Context API y librerías de estado no es binaria ni definitiva. Aquí tienes las recomendaciones finales:

💡 Para principiantes: Empieza con Context API. Domina el estado local y el paso de props antes de añadir complejidad. Muchas aplicaciones nunca necesitan más que esto.
💡 Para proyectos medianos: Considera Zustand. Es simple, pequeño y solve problemas reales de rendimiento que Context tiene. La migración desde Context es gradual.
💡 Para proyectos grandes/equipos: Redux Toolkit es el estándar probado. La inversión en aprenderlo se paga en mantenibilidad y herramientas de desarrollo.
Ver más: Recursos para profundizar
  • Documentación oficial de React Context: react.dev/reference/react/createContext
  • Zustand GitHub: github.com/pmndrs/zustand
  • Redux Toolkit: redux-toolkit.js.org
  • Dan Abramov sobre Context vs Redux: nunca necesitas Redux si Context resuelve tu problema.
🧠 Quiz: Context vs Librerías de Estado

Estás construyendo un editor de texto colaborativo en tiempo real donde múltiples usuarios editando el mismo documento ven los cambios instantáneamente. ¿Cuál sería la mejor elección?

  • A) Context API, porque es nativo de React
  • B) Zustand o Redux, porque necesitas actualizaciones frecuentes y selectores optimizados
  • C) Solo estado local con useState
✅ Respuesta correcta: B. Un editor colaborativo requiere actualizaciones frecuentes, optimización de re-renders, y probablemente middleware para sincronización con el servidor. Context causaría re-renders excesivos. Zustand o Redux con websockets son ideales.
🧠 Quiz: ¿Cuándo usar Context?

¿Cuál de los siguientes casos es MEJOR para Context API que para una librería de estado dedicada?

  • A) Un carrito de compras con 50 productos
  • B) El tema claro/oscuro de toda la aplicación
  • C) Una lista de tareas con filtros complejos
✅ Respuesta correcta: B. El tema de la aplicación es un caso clásico para Context: cambia raramente, todos los componentes lo necesitan, y la lógica es simple (toggle entre dos valores). No justifica la complejidad de Redux o Zustand.

En la próxima lección, profundizaremos en la implementación práctica de Context API con patrones avanzados como múltiples contextos, contextos dinámicos y técnicas de optimización de rendimiento. Dominaremos cuándo y cómo crear cada contexto para maximizar la eficiencia de tu aplicación.