react-context-api

Cómo usar el Context API en React

  • 4 min

Context API es un mecanismo que React proporciona para compartir datos entre componentes sin tener que pasar props manualmente a través de cada nivel del árbol de componentes.

En el artículo anterior vimos un problema habitual en las Apps de React, el Prop Drilling. Vimos que pasar datos a través de 10 componentes que no los necesitan es ineficiente y difícil de mantener.

En lugar de esto, Context API permite que los componentes accedan directamente a los datos que necesitan, sin importar en qué nivel del árbol se encuentren.

El Contexto es como un sistema de radiodifusión

  • Tenemos una Emisora (Provider) en la parte alta de la aplicación que emite datos.
  • Cualquier componente, esté donde esté en el árbol, puede sintonizar esa emisora usando un Receptor (Consumer) y acceder a los datos directamente

¿Cuándo usar Context?

Context es muy práctico, pero tiene un coste de rendimiento muy alto por re-renderizado. Cada vez que el valor del Contexto cambia (ej: cambiamos el tema), TODOS los componentes que consumen ese contexto se re-renderizan.

✅ Context es ideal para
  • Datos que cambian con poca frecuencia: Tema, Idioma, Usuario Autenticado.
  • Datos estáticos de configuración.
❌ No es recomendable para
  • Estados de alta frecuencia: Inputs de teclado, posiciones del ratón, datos de tiempo real que cambian 60 veces por segundo. Para eso provocaríamos un lag tremendo en la app.

¿Cómo funciona Context API?

Context API se basa en tres conceptos principales:

Crear el contexto (createContext)

ContextoEs un objeto que contiene los datos que deseas compartir.

Proveer el valor (Provider)

Es un componente que envuelve a los componentes que necesitan acceder al contexto. Proporciona los datos a sus componentes hijos.

Consumir el valor (useContext)

Es un Hook que permite a los componentes hijos acceder a los datos del contexto.

Vamos a implementar el ejemplo más clásico y útil para entender esto: Un gestor de Tema (Claro / Oscuro).

Paso 1: El patrón “Custom Provider”

Aunque podríamos crear el contexto directamente en App.jsx, vamos a crear un archivo dedicado para gestionar toda la lógica del tema.

Creamos src/context/ThemeContext.jsx:

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

// 1. CREAR EL CONTEXTO
// El valor inicial (null) solo se usa si intentamos acceder al contexto 
// SIN un provider (útil para tests, pero raro en producción).
const ThemeContext = createContext(null);

// 2. CREAR EL COMPONENTE PROVIDER (La "Nube")
export function ThemeProvider({ children }) {
  // Aquí guardamos el estado global
  const [theme, setTheme] = useState('light'); // 'light' | 'dark'

  const toggleTheme = () => {
    setTheme((curr) => (curr === 'light' ? 'dark' : 'light'));
  };

  // El valor que compartiremos con toda la app
  const contextValue = {
    theme,
    toggleTheme
  };

  return (
    // Envolvemos los hijos con el Provider y le pasamos el valor
    <ThemeContext.Provider value={contextValue}>
      {children}
    </ThemeContext.Provider>
  );
}

// 3. CUSTOM HOOK PARA CONSUMIR (La "Antena")
// Esto es una buena práctica para no tener que importar useContext y el Contexto
// en cada componente. Abstraemos la lógica aquí.
export const useTheme = () => {
  const context = useContext(ThemeContext);
  
  // Validación de seguridad:
  if (!context) {
    throw new Error("useTheme debe usarse dentro de un ThemeProvider");
  }
  
  return context;
};
Copied!

Toda la lógica del tema (estados y funciones para cambiarlo) vive aquí, aislada del resto de la app.

Paso 2: Conectar el Provider

Ahora tenemos que ir a la raíz de nuestra aplicación (generalmente main.jsx o App.jsx) y envolver todo con nuestro ThemeProvider.

// src/App.jsx
import { ThemeProvider } from './context/ThemeContext';
import Layout from './components/Layout';

function App() {
  return (
    // Todos los componentes dentro de ThemeProvider tendrán acceso al tema
    <ThemeProvider>
      <Layout />
    </ThemeProvider>
  );
}
Copied!

Paso 3: Consumir los datos

Ahora llega la parte interesante. Imagina un botón que está muy profundo en la jerarquía, dentro de LayoutHeaderUserMenuThemeButton.

Ya no necesitamos pasar props. Simplemente usamos nuestro hook useTheme.

// src/components/ThemeButton.jsx
import { useTheme } from '../context/ThemeContext';

export default function ThemeButton() {
  // "Teletransportamos" los datos y la función aquí
  const { theme, toggleTheme } = useTheme();

  return (
    <button 
      onClick={toggleTheme}
      style={{
        backgroundColor: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#000' : '#fff'
      }}
    >
      Cambiar a modo {theme === 'light' ? 'Oscuro' : 'Claro'}
    </button>
  );
}
Copied!

Hemos conectado el botón con el estado global sin tocar ningún componente intermedio 🎉.

Múltiples contextos

Podemos anidar tantos Providers como queramos. Es común ver apps así:

<AuthProvider>
  <ThemeProvider>
    <LanguageProvider>
      <App />
    </LanguageProvider>
  </ThemeProvider>
</AuthProvider>
Copied!

Esta estructura mantiene la lógica separada: el tema no sabe nada del usuario, y el usuario no sabe nada del idioma.