Hook useContext
Lee y suscríbete a contexto desde cualquier componente sin prop drilling.
Busca en todas las páginas de la documentación
Lee y suscríbete a contexto desde cualquier componente sin prop drilling.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
// 1. Crea el contexto
const ThemeContext = createContext<Theme>("light");
// 2. Proporciónalo
<ThemeContext.Provider value={theme}>
<App />
</ThemeContext.Provider>
// 3. Consúmelo
const theme = useContext(ThemeContext);Cuándo usarlo: Necesitas compartir valores (tema, autenticación, configuración regional) en muchos componentes sin pasar props en cada nivel.
"use client";
import { createContext, useContext, useState, type ReactNode } from "react";
type Theme = "light" | "dark";
const ThemeContext = createContext<Theme>("light");
const ThemeToggleContext = createContext<() => void>(() => {});
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>("light");
const toggle = () => setTheme((t) => (t === "light" ? "dark" : "light"));
return (
<ThemeContext.Provider value={theme}>
<ThemeToggleContext.Provider value={toggle}>
{children}
</ThemeToggleContext.Provider>
</ThemeContext.Provider>
);
}
export function ThemeDisplay() {
const theme = useContext(ThemeContext);
const toggle = useContext(ThemeToggleContext);
return (
<div className={theme === "dark" ? "bg-gray-900 text-white p-4" : "bg-white text-black p-4"}>
<p>Tema actual: {theme}</p>
<button onClick={toggle} className="mt-2 px-3 py-1 border rounded">
Alternar Tema
</button>
</div>
);
}Lo que esto demuestra:
createContext y ProvideruseContext en un componente profundamente anidadouseContext encuentra el Provider más cercano por encima del componente que llama en el árbolvalue del proveedor, cada componente que consume ese contexto se re-renderizacreateContextObject.is) para detectar cambios - una nueva referencia de objeto dispara re-renderizados incluso si el contenido es idéntico| Parámetro | Tipo | Descripción |
|---|---|---|
context | Context<T> | El objeto de contexto creado por createContext |
| Retorno | Tipo | Descripción |
|---|---|---|
value | T | El valor actual del contexto del proveedor más cercano |
Contexto con un hook personalizado (patrón recomendado):
const AuthContext = createContext<AuthState | null>(null);
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth debe usarse dentro de un AuthProvider");
return ctx;
}Múltiples contextos compuestos:
export function AppProviders({ children }: { children: ReactNode }) {
return (
<AuthProvider>
<ThemeProvider>
<LocaleProvider>
{children}
</LocaleProvider>
</ThemeProvider>
</AuthProvider>
);
}Contexto con reductor para estado complejo:
const DispatchContext = createContext<Dispatch<Action>>(() => {});
const StateContext = createContext<AppState>(initialState);// Siempre tipifica tu contexto - evita `any`
const UserContext = createContext<User | null>(null);
// Usa un hook personalizado para estrechar el tipo
export function useUser(): User {
const user = useContext(UserContext);
if (!user) throw new Error("useUser requiere un UserProvider");
return user; // estrechado a User, nunca null
}Avalancha de re-renderizado - Pasar un literal de objeto nuevo como value en cada renderizado hace que todos los consumidores se re-renderizen. Solución: Memoiza el valor con useMemo o divide en contextos separados.
Proveedor Faltante - Olvidar el Provider devuelve silenciosamente el valor predeterminado, que puede ser undefined. Solución: Usa un hook personalizado que lance una excepción si el contexto es null.
Uso excesivo de contexto para actualizaciones de alta frecuencia - El contexto no está optimizado para valores que cambian en cada pulsación de tecla o fotograma de animación. Solución: Usa useSyncExternalStore, Zustand o Jotai para estado compartido de alta frecuencia.
Confusión de Valor Predeterminado - El valor predeterminado en createContext(defaultValue) solo se usa cuando no hay proveedor, no como estado inicial para el proveedor. Solución: Siempre envuelve los componentes consumidores en el proveedor apropiado.
| Alternativa | Úsalo Cuando | No lo Uses Cuando |
|---|---|---|
| Prop drilling | Solo 1-2 niveles de profundidad y pocos consumidores | Muchos niveles o muchos consumidores |
| Zustand / Jotai | Actualizaciones de alta frecuencia o estado compartido complejo | Valores simples e infrecuentes como tema o configuración regional |
| Composición de componentes | Los hijos pueden ser pasados como props para evitar que componentes intermedios necesiten los datos | Los datos se necesitan en muchos niveles arbitrarios |
use(Context) (React 19) | Quieres leer contexto condicionalmente o en bucles | Necesitas soportar React 18 o versiones anteriores |
¿Por qué no siempre usar Zustand? El contexto está integrado, no requiere ninguna dependencia y es la herramienta correcta para valores de baja frecuencia limitados al árbol como tema, autenticación o configuración regional.
De una aplicación SaaS de Next.js 15 / React 19 en producción (SystemsArchitect.io).
// Ejemplo de producción: ToastProvider con createContext y hook de guarda
// Archivo: src/components/toast-provider.tsx
'use client';
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
interface Toast {
id: string;
message: string;
type: 'success' | 'error' | 'info';
}
interface ToastContextValue {
toasts: Toast[];
showToast: (message: string, type?: Toast['type']) => void;
dismissToast: (id: string) => void;
}
const ToastContext = createContext<ToastContextValue | null>(null);
export function ToastProvider({ children }: { children: ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([]);
const showToast = useCallback((message: string, type: Toast['type'] = 'info') => {
const id = crypto.randomUUID();
setToasts((prev) => [...prev, { id, message, type }]);
setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, 5000);
}, []);
const dismissToast = useCallback((id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, []);
return (
<ToastContext.Provider value={{ toasts, showToast, dismissToast }}>
{children}
<div className="fixed bottom-4 right-4 space-y-2 z-50">
{toasts.map((toast) => (
<div key={toast.id} className="rounded bg-gray-900 text-white px-4 py-2 shadow">
{toast.message}
<button onClick={() => dismissToast(toast.id)} className="ml-2">x</button>
</div>
))}
</div>
</ToastContext.Provider>
);
}
// Hook de guarda: lanza una excepción si se usa fuera del proveedor
export function useToast(): ToastContextValue {
const ctx = useContext(ToastContext);
if (!ctx) {
throw new Error('useToast debe usarse dentro de un ToastProvider');
}
return ctx;
}Lo que esto demuestra en producción:
createContext<ToastContextValue | null>(null) usa null como valor predeterminado para que el hook de guarda pueda detectar cuándo se usa fuera de un proveedor. Usar un valor predeterminado real fallaría silenciosamente en lugar de lanzar un error útil.useCallback en showToast y dismissToast mantiene estables estas referencias de función en todos los renderizados. Sin él, cualquier componente que consume useToast() se re-renderizaría en cada cambio de estado de toast porque el objeto de valor de contexto contendría nuevas referencias de función.setToasts((prev) => ...)) en lugar de leer toasts directamente. Esto evita agregar toasts al array de dependencias de useCallback, lo que rompería la referencia estable.setTimeout para descartar automáticamente captura el id en su cierre y utiliza un actualizador funcional para filtrar. Esto evita errores de cierre obsoleto donde el array de toasts ha cambiado cuando el tiempo de espera se activa.useToast() estrecha el tipo de ToastContextValue | null a ToastContextValue, por lo que los consumidores nunca necesitan manejar null. El throw asegura un mensaje de error claro durante el desarrollo si el proveedor falta.createContext(defaultValue).undefined o null, tu componente puede fallar silenciosamente.null para capturar esto durante el desarrollo.const MyContext = createContext<MyType | null>(null);
export function useMyContext(): MyType {
const ctx = useContext(MyContext);
if (!ctx) throw new Error("useMyContext debe usarse dentro de un Proveedor");
return ctx; // estrechado a MyType, nunca null
}Object.is para detectar cambios. Pasar un literal de objeto nuevo { theme, toggle } como valor del proveedor crea una nueva referencia en cada renderizado.useMemo:const value = useMemo(() => ({ theme, toggle }), [theme, toggle]);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;useSyncExternalStore, Zustand o Jotai en su lugar.createContext(default) se usa solo cuando no hay un Proveedor encima del consumidor.value del Proveedor es el valor real en tiempo de ejecución pasado a los consumidores.function AppProviders({ children }: { children: ReactNode }) {
return (
<AuthProvider>
<ThemeProvider>
<LocaleProvider>
{children}
</LocaleProvider>
</ThemeProvider>
</AuthProvider>
);
}composeProviders que reduzca un array de proveedores.use(Context) es nuevo en React 19 y puede ser llamado dentro de condiciones y bucles, a diferencia de useContext.useContext es el enfoque estándar si necesitas compatibilidad con React 18.useCallback mantiene sus referencias estables en todos los renderizados.// Usa el patrón null default + guard hook
const UserContext = createContext<User | null>(null);
// El hook de guarda estrecha el tipo
export function useUser(): User {
const user = useContext(UserContext);
if (!user) throw new Error("useUser requiere un UserProvider");
return user;
}use() de React 19 puede leer contexto condicionalmenteuseContext en un hook personalizado para seguridad de tipoRevisado por Chris St. John·Última actualización: 16 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥