createContext y Provider: proveer un valor por el árbol
createContext fabrica un canal —un objeto con id, Provider y valor por defecto— por el que un valor desciende por el árbol de owners sin pasar por props. El Provider fija ese valor para un subárbol, no crea nodo en el DOM y lo hace una sola vez: entender el valor por defecto y esa fijación única es la base de todo el nivel.
El problema que resuelve: el prop drilling
Considera un Boton sepultado ocho niveles bajo la raíz que necesita conocer el tema activo. Sin contexto, el tema tendría que declararse como prop en Boton y en los siete ancestros que lo contienen, aunque a ninguno de ellos le incumba. Cada componente intermedio se convierte en una tubería que transporta un dato ajeno, y añadir un nuevo consumidor obliga a modificar toda la cadena. Es acoplamiento estructural puro: la forma del árbol dicta qué sabe cada pieza.
// Sin contexto: el tema cruza niveles que no lo usan, solo para llegar al Boton<Barra tema={tema}> <Menu tema={tema}> <Boton tema={tema} /> {/* por fin alguien lo usa */} </Menu></Barra>;El contexto rompe ese acoplamiento. Un ancestro provee un valor y cualquier descendiente, a la profundidad que sea, lo consume directamente. Los intermediarios desaparecen de la ecuación: ni lo declaran, ni lo reenvían, ni se enteran. El árbol de owners —que ya conoces— es también el canal por el que viaja el contexto; aquí lo usamos a propósito.
createContext: el canal y su valor por defecto
createContext no crea estado ni reactividad: fabrica un identificador con una envoltura para proveerlo. Devuelve un objeto con tres campos.
import { createContext } from "solid-js";const TemaContext = createContext<"claro" | "oscuro">("claro");// ^ objeto con { id, Provider, defaultValue }El id es un symbol único: la clave bajo la que el valor se guarda en el campo context de cada owner. Por ser un símbolo, dos contextos distintos jamás colisionan aunque tengan la misma forma. El Provider es el componente que inyecta un valor. Y defaultValue es lo que reciben los consumidores que no tienen ningún Provider por encima.
Ese valor por defecto es una decisión de diseño, no un relleno. Dos filosofías compiten. Una es dar un default con sentido —un tema claro, un logger que no hace nada, una configuración neutra— para que el componente funcione aislado, sin Provider, útil en pruebas y en catálogos de componentes. La otra es no dar default —lo veremos en las próximas lecciones— para que la ausencia de Provider sea un error detectable en vez de un silencio. Elegir uno u otro es declarar si tu contexto es opcional u obligatorio.
// Default con sentido: un logger que no hace nada.// Sin Provider el componente funciona; con Provider, se inyecta el real.const LogContext = createContext({ log: (_: string) => {} });// Sin default: la ausencia de Provider sera un error, no un silencio.const SesionContext = createContext<Sesion>(); // Sesion | undefinedEl primer contexto es opcional: cualquiera puede consumirlo y, sin proveedor, recibe un logger inofensivo. El segundo es obligatorio: su tipo arrastra undefined para forzarte a decidir qué hacer cuando falta el proveedor —el tema de las dos próximas lecciones—.
Provider: inyectar un valor en un subárbol
El Provider es un componente que recibe una prop value y envuelve a unos children. Todo lo que quede dentro —a cualquier profundidad— verá ese valor al consumir el contexto.
function App() { return ( <TemaContext.Provider value="oscuro"> <Cabecera /> <Contenido /> </TemaContext.Provider> );}Dos hechos definen su naturaleza. El primero: el Provider no genera ningún nodo en el DOM. No es un div envolvente; es un owner que fija una entrada en su campo context y renderiza sus hijos tal cual. No altera el layout ni el HTML resultante. El segundo: el alcance es exactamente el subárbol que envuelve. Fuera de él, el contexto vuelve al default o al Provider que corresponda más arriba.
Como el valor se resuelve trepando por el árbol de owners, anidar dos providers del mismo contexto hace que el más interno gane para su subárbol: sobrescribe la entrada por su id. Es la base de patrones como un tema global con una sección que lo invierte, que veremos en la última lección.
El valor se fija una sola vez
Aquí está la sutileza que separa a Solid de React y que hay que interiorizar desde el primer día. Cuando el Provider se monta, lee props.value una vez, sin suscribirse, y lo guarda en el contexto del owner. No vuelve a observar esa prop. Si más tarde cambias lo que pasas a value, los consumidores no se enteran: en Solid no hay re-render que los vuelva a ejecutar.
// ESTO NO ES REACTIVO: al cambiar el signal, los consumidores no ven el cambioconst [tema, setTema] = createSignal("claro");<TemaContext.Provider value={tema()}>...</TemaContext.Provider>;// ^ se lee una vez y se congela ese string// ESTO SI ES REACTIVO: provees el accesor, no su lectura<TemaContext.Provider value={tema}>...</TemaContext.Provider>;// ^ el consumidor llama a tema() y se suscribe elLa regla es nítida: para que un contexto sea reactivo, el valor que provees debe ser reactivo por dentro —un signal, un store, un objeto con accesores— y el consumidor obtiene la reactividad al leerlo, no al recibirlo. El contexto transporta la referencia; la reactividad vive en lo transportado. Las próximas lecciones construyen sobre esto: primero cómo consumir, luego cómo tipar, y después el patrón canónico de meter un store en el canal.