Múltiples providers, composición y rendimiento
Un contexto por preocupación, providers compuestos sin caer en la pirámide de anidamiento, y el hecho que lo cambia todo respecto a React: en Solid el contexto no provoca re-render, porque no hay render. La lectura es una búsqueda única por owners y la reactividad vive solo en los signals y stores que metes dentro. De ahí se deduce cuándo conviene elevar un Provider y cuándo acotarlo.
Un contexto por preocupación
La primera regla es de diseño, no de rendimiento: un contexto por eje de estado independiente. La sesión no tiene que ver con el tema, ni el tema con el carrito; mezclarlos en un solo contexto acopla lo que cambia por razones distintas y a ritmos distintos. Cada preocupación crea su propio createContext, su Provider y su hook useX, y se compone con los demás por anidamiento.
function App() { return ( <SesionProvider> <TemaProvider> <CarritoProvider> <Rutas /> </CarritoProvider> </TemaProvider> </SesionProvider> );}Cada Provider envuelve al siguiente, y Rutas —con todo lo que cuelga de ella— consume cualquiera de los tres desde la profundidad que sea. El orden de anidamiento solo importa si un provider necesita consumir a otro: el interno puede leer al externo, nunca al revés.
Componer sin la pirámide
Con cuatro o cinco contextos, ese anidamiento degenera en una escalera incómoda —la pirámide de providers—. Se aplana de dos formas. La sencilla: un componente Providers que encapsula la escalera y se usa una vez en la raíz.
const Providers: ParentComponent = (props) => ( <SesionProvider> <TemaProvider> <CarritoProvider>{props.children}</CarritoProvider> </TemaProvider> </SesionProvider>);La genérica, cuando la lista crece o se arma dinámicamente: un combinador que pliega un array de providers en uno solo.
import { type ParentComponent, type JSX } from "solid-js";function combinar(...proveedores: ParentComponent[]): ParentComponent { return (props) => proveedores.reduceRight( (hijos, Prov) => <Prov>{hijos}</Prov>, (<>{props.children}</>) as JSX.Element, );}const Providers = combinar(SesionProvider, TemaProvider, CarritoProvider);El reduceRight respeta el orden: el primero de la lista queda más externo. Un combinador así convierte la composición de contextos en datos —una lista— en vez de estructura anidada a mano.
El contexto no provoca re-render
Este es el corazón del nivel y el punto donde Solid rompe con React de forma más tajante. En React, cambiar el value de un Provider re-renderiza todos los consumidores de ese contexto, aunque solo les interese una parte; de ahí nace toda una industria de mitigaciones: trocear contextos, memoizar el value, usar selectores. En Solid, nada de eso existe, porque nada se re-renderiza.
La lectura con useContext es una búsqueda única por el árbol de owners que ocurre una vez, cuando el componente se crea. Devuelve una referencia y ahí acaba su papel. La reactividad no la aporta el contexto: la aportan los signals y stores que viajan dentro. Un consumidor se actualiza —de forma quirúrgica— solo cuando cambia la hoja reactiva concreta que leyó, nunca porque “el contexto cambió”.
La consecuencia práctica es liberadora: puedes permitirte un contexto “gordo”. Meter un store rico con veinte campos en un solo contexto no penaliza a nadie, porque el grano fino del store notifica campo por campo. El consejo de React de partir contextos para evitar renders no solo es innecesario en Solid: es contraproducente, porque fragmenta sin motivo lo que conceptualmente va junto. En Solid, la granularidad la da el grafo reactivo, no la frontera del contexto.
Esto no te autoriza a meterlo todo en un canal: la regla de una preocupación por contexto sigue siendo buena arquitectura. Lo que cambia es el motivo por el que separas. Ya no divides por miedo al rendimiento —ese impuesto no existe—, sino por cohesión y por ciclo de vida: la sesión y el carrito nacen y mueren en momentos distintos, y mezclarlos en un contexto los ataría sin razón. Un contexto por eje conceptual, cada uno tan gordo como ese eje pida.
Cuándo elevar el Provider
Que el contexto no cueste renders no significa subir todo a la raíz. La altura correcta de un Provider es el ancestro común más bajo de sus consumidores. Tres casos cubren casi todo.
El caso “por instancia” es el más revelador, porque el mismo Provider montado varias veces da estados aislados sin una línea extra: la posición en el árbol es la identidad del estado.
function Panel() { return ( <> {/* dos acordeones, cada uno con su propio estado abierto/cerrado */} <AcordeonProvider><Seccion titulo="Envío" /></AcordeonProvider> <AcordeonProvider><Seccion titulo="Pago" /></AcordeonProvider> </> );}El error en un sentido es elevar de más: subir a la raíz un estado que solo usa una pantalla lo mantiene vivo toda la sesión y lo hace único cuando quizá querías uno por instancia. El error opuesto es acotar de menos: si dos primos necesitan el mismo estado y pones el Provider en uno de ellos, el otro no lo alcanza. La brújula es siempre la misma: localiza a todos los consumidores, encuentra su ancestro común más bajo, y pon ahí el Provider. Ni un nivel más arriba —o pierdes aislamiento y limpieza—, ni uno más abajo —o dejas consumidores fuera de alcance—. Que valga la pena usar contexto para ese estado fue la decisión del nivel anterior; este resuelve cómo proveerlo bien una vez decidido que sí.