¿Qué es el prop drilling y cómo lo resuelve Context?
Ver respuesta — intenta responderla en voz alta primero
Prop drilling es pasar una prop por varios niveles de componentes que no la usan, solo para que llegue a uno muy abajo. Context permite que cualquier descendiente lea un valor directamente desde un Provider, sin pasar por los intermedios.
Imagina que el tema visual de la app vive en la raíz y lo necesita un botón cinco niveles más abajo. Sin Context, los cuatro componentes intermedios reciben y reenvían una prop que no les importa. Eso es prop drilling: ensucia las firmas y hace doloroso cualquier cambio.
Context crea un "canal" directo:
createContextdefine el canal y su tipo.- El Provider envuelve un árbol y publica el valor.
useContext(dentro del hookuseTemade abajo) lo lee desde cualquier descendiente.- Un custom hook que lanza error fuera del Provider evita usar el contexto sin configurarlo.
Cuándo NO usarlo: Context no es para todo el estado. Es ideal para datos globales que cambian poco (tema, idioma, usuario autenticado). Para estado local basta useState, y como cada cambio del valor re-renderiza a todos los consumidores, no metas ahí estado que cambia con cada tecla.
import { createContext, useContext, useState, type ReactNode } from "react";
type Tema = "claro" | "oscuro";
type ContextoTema = { tema: Tema; alternarTema: () => void };
// undefined como valor inicial: obliga a usar el Provider
const TemaContext = createContext<ContextoTema | undefined>(undefined);
export function ProveedorTema({ children }: { children: ReactNode }) {
const [tema, setTema] = useState<Tema>("claro");
const alternarTema = () =>
setTema((actual) => (actual === "claro" ? "oscuro" : "claro"));
// En React 19 el Context se usa directamente como Provider
return <TemaContext value={{ tema, alternarTema }}>{children}</TemaContext>;
}
// Custom hook: falla rápido si se usa fuera del Provider
export function useTema(): ContextoTema {
const contexto = useContext(TemaContext);
if (contexto === undefined) {
throw new Error("useTema debe usarse dentro de <ProveedorTema>");
}
return contexto; // aquí TypeScript ya sabe que no es undefined
}
// Consumidor profundo: sin pasar props por niveles intermedios
function BotonTema() {
const { tema, alternarTema } = useTema();
return <button onClick={alternarTema}>Tema actual: {tema}</button>;
}
"Engañar" a TypeScript con un valor inicial falso para no lidiar con undefined:
// MAL: compila, pero si olvidas el Provider, alternarTema no existe
// y truena en runtime con un error confuso
const TemaContext = createContext<ContextoTema>({} as ContextoTema);
La corrección es el patrón del ejemplo: valor inicial undefined y un custom hook (useTema) que lanza un error claro si no hay Provider. El bug se detecta al instante y con un mensaje que dice exactamente qué falta.
Prop drilling es cuando paso una prop por varios componentes intermedios que no la usan, solo para llegar a uno muy abajo. Context lo resuelve creando un canal directo: envuelvo el árbol con un Provider y cualquier descendiente lee el valor con
useContext. En TypeScript lo tipo conundefinedcomo valor inicial y expongo un custom hook que lanza error si se usa fuera del Provider. Eso sí, no uso Context para todo: es para datos globales que cambian poco, como tema o usuario, porque cada cambio re-renderiza a todos los consumidores.
Crea un contexto de idioma con idioma: "es" | "en" y cambiarIdioma, junto con su hook useIdioma() que lance error fuera del Provider.
Ver solución
import { createContext, useContext, useState, type ReactNode } from "react";
type Idioma = "es" | "en";
type ContextoIdioma = { idioma: Idioma; cambiarIdioma: (nuevo: Idioma) => void };
const IdiomaContext = createContext<ContextoIdioma | undefined>(undefined);
export function ProveedorIdioma({ children }: { children: ReactNode }) {
const [idioma, setIdioma] = useState<Idioma>("es");
return (
<IdiomaContext value={{ idioma, cambiarIdioma: setIdioma }}>
{children}
</IdiomaContext>
);
}
export function useIdioma(): ContextoIdioma {
const contexto = useContext(IdiomaContext);
if (contexto === undefined) {
throw new Error("useIdioma debe usarse dentro de <ProveedorIdioma>");
}
return contexto;
}