TechLead

Gestion de Estado

Context API, Redux y Zustand para el estado global

Gestion de Estado Global

Cuando multiples componentes necesitan compartir estado, necesitas una solucion de gestion de estado global. Hay varias opciones segun la complejidad de tu aplicacion.

Context API

import { createContext, useContext, useState } from 'react';

// Crear contexto
const AuthContext = createContext();

// Proveedor
export function AuthProvider({ children }) {
  const [usuario, setUsuario] = useState(null);

  const login = (datosUsuario) => setUsuario(datosUsuario);
  const logout = () => setUsuario(null);

  return (
    <AuthContext.Provider value={{ usuario, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// Hook personalizado para usar el contexto
export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth debe usarse dentro de AuthProvider');
  }
  return context;
}

// Uso
function BarraNav() {
  const { usuario, logout } = useAuth();
  return (
    <nav>
      {usuario ? (
        <>
          <span>Hola, {usuario.nombre}</span>
          <button onClick={logout}>Salir</button>
        </>
      ) : (
        <Link to="/login">Iniciar sesion</Link>
      )}
    </nav>
  );
}

Zustand (Recomendado)

// npm install zustand

import { create } from 'zustand';

// Crear store
const useStore = create((set) => ({
  cuenta: 0,
  incrementar: () => set((state) => ({ cuenta: state.cuenta + 1 })),
  decrementar: () => set((state) => ({ cuenta: state.cuenta - 1 })),
  reiniciar: () => set({ cuenta: 0 })
}));

// Uso en componentes
function Contador() {
  const { cuenta, incrementar, decrementar, reiniciar } = useStore();

  return (
    <div>
      <p>Cuenta: {cuenta}</p>
      <button onClick={incrementar}>+</button>
      <button onClick={decrementar}>-</button>
      <button onClick={reiniciar}>Reiniciar</button>
    </div>
  );
}

// Seleccionar estado especifico (evita re-renders innecesarios)
function MostrarCuenta() {
  const cuenta = useStore((state) => state.cuenta);
  return <span>{cuenta}</span>;
}

Zustand con Acciones Asincronas

const useTareasStore = create((set, get) => ({
  tareas: [],
  cargando: false,
  error: null,

  obtenerTareas: async () => {
    set({ cargando: true, error: null });
    try {
      const respuesta = await fetch('/api/tareas');
      const tareas = await respuesta.json();
      set({ tareas, cargando: false });
    } catch (error) {
      set({ error: error.message, cargando: false });
    }
  },

  agregarTarea: async (titulo) => {
    const respuesta = await fetch('/api/tareas', {
      method: 'POST',
      body: JSON.stringify({ titulo })
    });
    const nuevaTarea = await respuesta.json();
    set((state) => ({ tareas: [...state.tareas, nuevaTarea] }));
  },

  eliminarTarea: (id) => {
    set((state) => ({
      tareas: state.tareas.filter(t => t.id !== id)
    }));
  }
}));

Cuando Usar Cada Opcion

Solucion Usar cuando
Props Estado local entre padre e hijo
Context Estado simple compartido (tema, auth)
Zustand Estado complejo, rendimiento importa
Redux Apps muy grandes, necesidad de middleware

Mejores Practicas

  • Empieza simple: Usa estado local primero
  • No sobreuses estado global: Solo para datos realmente compartidos
  • Divide stores: Un store por dominio (auth, carrito, etc.)