modifyMutable con produce y reconcile: lotes controlados
Varias asignaciones directas sobre un mutable son varias propagaciones; modifyMutable las funde en una sola aplicando un modificador dentro de un batch. produce te da un borrador que mutas imperativamente y notifica una vez; reconcile difunde un valor nuevo contra el estado actual y aplica solo los cambios mínimos, preservando identidad. La pieza que devuelve control y coherencia a la escritura libre de createMutable.
El problema: muchas escrituras, muchas pasadas
Fuera de una computación, cada asignación a un mutable abre y cierra su propia propagación. Si actualizas tres campos relacionados en sucesión, los memos y efectos que dependen de ellos corren tres veces, y en dos de esas veces observan un estado a medio construir. No es incorrección —el resultado final es bueno— sino trabajo redundante y coherencia rota para los observadores.
import { createMutable } from "solid-js/store";const state = createMutable({ nombre: "Ada", apellido: "Byron", edad: 36 });// Tres asignaciones sueltas -> tres propagacionesstate.nombre = "Grace"; // pasada 1state.apellido = "Hopper"; // pasada 2 (aqui alguien ve "Grace Byron")state.edad = 85; // pasada 3Ese "Grace Byron" transitorio es el síntoma. Conceptualmente las tres líneas son un solo cambio —“pasamos de una persona a otra”— pero el sistema las ve como tres eventos porque nada le dice que forman una unidad.
modifyMutable: un lote alrededor del modificador
modifyMutable(mutable, modificador) recibe el proxy y una función modificadora, y aplica sus cambios dentro de un único batch. Los consumidores se recalculan una sola vez, con el estado ya completo. Su firma —modifyMutable<T>(mutable: T, modifier: (state: T) => T): void— revela que el segundo argumento es justo lo que devuelven produce y reconcile: modificadores que transforman el estado.
import { modifyMutable, produce } from "solid-js/store";// Una sola propagacion: nadie ve el estado intermediomodifyMutable(state, produce((s) => { s.nombre = "Grace"; s.apellido = "Hopper"; s.edad = 85;}));Es la misma relación que batch tiene con los signals, adaptada al mundo del store: declaras que un conjunto de escrituras es semánticamente una, y el sistema recorre el grafo una vez en lugar de tantas como mutaciones hiciste.
produce: escribir el cambio como un borrador
produce(fn) devuelve un modificador que te entrega un borrador del estado —un proxy vivo solo dentro de la función— sobre el que escribes con sintaxis imperativa plena. Cada mutación que hagas dentro se traduce en una notificación granular, pero todas se emiten juntas al cerrar el lote. Es el patrón para cambios que conoces campo a campo: sabes exactamente qué tocar y quieres tocarlo mutando.
import { modifyMutable, produce } from "solid-js/store";const estado = createMutable({ items: [] as { id: number; hecho: boolean }[] });modifyMutable(estado, produce((s) => { s.items.push({ id: 1, hecho: false }); s.items.push({ id: 2, hecho: false }); s.items[0].hecho = true; // mutas hondo, con naturalidad})); // todo propaga una vezLa virtud de produce es que no renuncias a nada de la ergonomía imperativa —push, asignación por índice, mutación anidada— y a cambio obtienes el control del lote. Es, en esencia, la misma herramienta que usarías con setStore en un createStore; modifyMutable es el puente que te deja aplicarla sobre un mutable.
reconcile: difundir un valor nuevo con cambios mínimos
reconcile(valor) resuelve un problema distinto. En lugar de que tú describas los cambios, le das el estado final completo y él calcula la diferencia contra el actual, aplicando solo las mutaciones mínimas necesarias. Su ganancia es la preservación de identidad: las partes que no cambiaron conservan su referencia, así que no se disparan sus lectores ni se recrean sus nodos del DOM. Es la herramienta canónica para reemplazar datos que llegan enteros de fuera —una respuesta de red— sin tirar y reconstruir todo el árbol.
import { modifyMutable, reconcile } from "solid-js/store";const estado = createMutable({ usuarios: [] as { id: number; nombre: string }[] });async function refrescar() { const frescos = await fetch("/api/usuarios").then((r) => r.json()); // Difunde el array nuevo contra el actual: solo cambian las filas distintas modifyMutable(estado, reconcile(frescos, { key: "id" }));}La opción key le dice a reconcile cómo emparejar elementos entre el estado viejo y el nuevo: con key: "id", una fila con el mismo id se considera la misma entidad y se actualiza en sitio en vez de recrearse. El resultado es que una lista que se refresca cada pocos segundos solo repinta las filas que de verdad cambiaron, preservando foco, scroll y estado local de las que no. Es la diferencia entre parpadear la tabla entera y editar quirúrgicamente sus celdas.