Ir al contenido
angel.library

Context API con un hook propio

Pasar una prop a través de cinco componentes que no la usan, solo para que llegue al sexto, es “prop drilling”. Context evita eso: un valor disponible para cualquier descendiente del Provider, sin pasarlo manualmente por cada nivel. No reemplaza a Zustand/Redux para estado global complejo — es ideal para lo que es genuinamente “de todo el árbol” (tema, usuario autenticado, idioma).

createContext necesita un valor por defecto (se usa solo si un componente lo lee sin estar envuelto en el Provider — por eso conviene que sea null y no un objeto falso, para poder detectar el error).

context/AuthContext.tsx
import { type ReactNode, createContext, useState } from "react"
interface AuthContextValue {
usuario: string | null
login: (nombre: string) => void
logout: () => void
}
export const AuthContext = createContext<AuthContextValue | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [usuario, setUsuario] = useState<string | null>(null)
const login = (nombre: string) => setUsuario(nombre)
const logout = () => setUsuario(null)
return (
<AuthContext.Provider value={{ usuario, login, logout }}>
{children}
</AuthContext.Provider>
)
}

Consumir AuthContext directo con useContext(AuthContext) en cada componente obliga a chequear null en todos lados y repite el import. Un hook custom centraliza esa validación una sola vez, y falla con un mensaje claro si alguien lo usa fuera del Provider — en vez de un undefined silencioso más adelante.

context/AuthContext.tsx
import { useContext } from "react"
export function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error("useAuth() debe usarse dentro de <AuthProvider>")
}
return context
}
App.tsx
<AuthProvider>
<Header />
<Main />
</AuthProvider>
Header.tsx
import { useAuth } from "../context/AuthContext"
export function Header() {
const { usuario, logout } = useAuth()
return usuario ? (
<button onClick={logout}>Salir ({usuario})</button>
) : (
<LoginButton />
)
}
PiezaQué hace
createContext<T | null>(null)Crea el contexto, tipado, con null como default para poder detectar mal uso
<Context.Provider value={...}>Hace disponible ese valor para todos sus descendientes
useContext(Context)Lee el valor — el hook custom lo envuelve y valida
Hook custom (useAuth, etc.)Un solo punto de import, con error claro si falta el Provider
  • Todo componente que lee el contexto se vuelve a renderizar cuando value cambia. Si el Provider agrupa datos con frecuencias muy distintas —por ejemplo, tema y actividad de usuario— sepáralos en dos contextos.
  • value={{ usuario, login, logout }} crea un objeto nuevo en cada render del proveedor. Si eso causa renders innecesarios en consumidores costosos, envuelve el objeto en useMemo.
  • Context no es un reemplazo de Zustand/Redux para estado que cambia mucho y en muchos lugares — para eso, ver Zustand: evita el re-render de todo el árbol que Context fuerza.