setStore por ruta: de la raíz a la hoja
El setter de un store no reemplaza el estado: navega hasta la propiedad exacta que quieres tocar mediante una lista de argumentos que forman una ruta, y escribe solo esa hoja. Cuando el valor final es un objeto y ya había un objeto, Solid fusiona superficialmente en vez de sustituir. Dominar la ruta y la fusión es dominar la escritura granular sin spread manual.
La ruta como lista de argumentos
createStore devuelve una tupla familiar: el estado —un proxy de solo lectura— y un setter. Pero ese setter no funciona como el de un signal. Su firma es variádica: todos los argumentos menos el último forman la ruta hacia una propiedad, y el último es el valor que se escribe en ella.
import { createStore } from "solid-js/store";const [estado, setEstado] = createStore({ usuario: { nombre: "Ada", edad: 30 }, tema: "oscuro",});setEstado("tema", "claro"); // estado.tema = "claro"setEstado("usuario", "nombre", "Grace"); // estado.usuario.nombre = "Grace"Lee setEstado("usuario", "nombre", "Grace") en voz alta: entra en usuario, entra en nombre, escribe la cadena. Cada string es un salto de nivel en el árbol; el último argumento es la carga. No hay spread, no hay reconstrucción de los niveles intermedios, no reemplazas usuario para cambiar su nombre. Y lo decisivo: solo se despierta quien lee estado.usuario.nombre. Los lectores de estado.usuario.edad o de estado.tema no reciben notificación alguna, porque el store instala un signal perezoso por propiedad y tú acabas de tocar exactamente uno.
Esa ruta es una dirección, no una copia del camino. El signal grueso te obligaba a reconstruir cada nivel del trayecto con spread sobre spread; el store te deja nombrar el destino y viaja solo. La verbosidad que en un signal crecía con la profundidad, aquí es constante: un argumento por nivel, ni uno más.
La lectura, en cambio, no pasa por el setter: accedes al proxy como a cualquier objeto. La asimetría es deliberada y hay que grabarla desde el principio.
const nombre = estado.usuario.nombre; // leer: acceso directo al proxysetEstado("usuario", "nombre", "Grace"); // escribir: siempre por el setterLeer estado.usuario.nombre dentro de un ámbito reactivo suscribe a esa hoja; escribir exige el canal del setter. Nunca al revés: el proxy es de solo lectura y asignarle directamente es un error que verás con detalle en la última lección de este nivel.
Fusión superficial: el objeto como parche
¿Y si el último argumento no es una hoja sino un objeto? Aquí aparece la regla que define el carácter del store: cuando el valor final es un objeto y en esa posición ya había otro objeto, Solid no reemplaza, fusiona. Copia clave por clave del nivel superior del objeto que le pasas y deja intacto todo lo que no mencionas.
// usuario era { nombre: "Ada", edad: 30 }setEstado("usuario", { edad: 31 });// ahora es { nombre: "Ada", edad: 31 } -> nombre se conservoNo tuviste que esparcir ...usuario para preservar nombre: la fusión lo hace por ti. El objeto que entregas se comporta como un parche, un conjunto de claves que se aplican sobre lo existente. Es el mismo principio en la raíz, donde el parche cae sobre el estado completo:
// Fusion en la raiz: parchea varias claves de primer nivel a la vezsetEstado({ tema: "claro", cargando: false });// usuario y cualquier otra clave del estado permanecen intactosPiensa siempre en términos de parche, no de reemplazo: al pasar un objeto no dices «el estado ahora es esto», dices «aplica estas claves sobre lo que ya hay». Ese cambio de mentalidad —de sustituir a parchear— es la mitad de lo que distingue escribir un store de escribir un signal, y explica por qué casi nunca necesitas spread para conservar lo que no tocas.
El límite del parche: la fusión no es profunda
La fusión es una espada de doble filo, y confundir su alcance es la fuente del primer bug serio con stores. Es superficial: fusiona solo el primer nivel del objeto que pasas. Cualquier objeto anidado dentro de ese parche reemplaza por completo a su homólogo, no se funde con él.
const [estado, setEstado] = createStore({ editor: { fuente: "mono", tema: { fondo: "negro", texto: "blanco" } },});// PELIGRO: tema anidado reemplaza entero, texto desaparecesetEstado("editor", { tema: { fondo: "gris" } });// editor.tema es ahora { fondo: "gris" } -> texto se perdio// BIEN: alarga la ruta hasta la hoja profundasetEstado("editor", "tema", "fondo", "gris"); // texto intactoLa regla operativa es nítida: la fusión te ahorra el spread de un nivel, no de todos. Si el campo que cambias vive a tres niveles de profundidad, extiende la ruta tres niveles y escribe la hoja; no entregues un objeto anidado esperando que Solid lo funda hacia dentro, porque no lo hará. La ruta larga es granular y segura; el objeto anidado es un reemplazo silencioso que borra hermanos.
Ten a mano un modelo mental para no vacilar en caliente: la fusión cubre un nivel de anidamiento, la ruta cubre tantos como argumentos le des. Cuando dudes de si un cambio profundo se fundirá o reemplazará, no adivines, alarga la ruta hasta la hoja y la ambigüedad se disuelve por completo.
// Ante la duda, ruta larga hasta la hoja: granular y sin borrar hermanossetEstado("editor", "tema", "texto", "gris"); // fondo sobreviveVarias claves hermanas en una escritura
Un segmento de la ruta no tiene que ser una sola clave: puede ser un array de claves, y entonces Solid aplica el resto de la ruta a cada una. Sobre un objeto, eso significa escribir varias propiedades hermanas de una sola vez, con el mismo valor o el mismo actualizador.
const [caja, setCaja] = createStore({ ancho: 100, alto: 100, margen: 8 });setCaja(["ancho", "alto"], 0); // ambas a 0 en una escriturasetCaja(["ancho", "alto", "margen"], 0); // restablece las tres de golpesetCaja(["ancho", "alto"], (v) => v * 2); // duplica las dos hermanasNo es azúcar cosmético: cada clave listada se escribe como su propia hoja y notifica a sus propios lectores, pero la operación entera se agrupa en un único lote, así que la propagación ocurre una vez. Es el gesto natural para restablecer un grupo de campos afines o aplicarles una misma transformación sin repetir el nombre del store por cada uno. En la lección 4 verás que ese mismo array de claves, cuando el contenedor es un array, se convierte en una lista de índices; la mecánica es idéntica, cambia solo la naturaleza de las claves.