Updating Nested State
Array paths, produce, and reconcile — the tools for making complex store updates precise instead of clumsy.
阅读需 3 分钟
The previous lesson introduced createStore and its path-based setter for a single nested field. Real applications usually need more than that: updating items inside arrays, applying several changes to an object at once, or replacing a store's contents wholesale after a network request. This lesson covers the tools for each case.
Path setters work through arrays too
const [todos, setTodos] = createStore([
{ id: 1, text: "Learn Solid", done: false },
{ id: 2, text: "Build an app", done: false },
]);
// Update by array index
setTodos(0, "done", true);
// Update by a matching predicate — every item where the condition is true
setTodos((todo) => todo.id === 2, "done", true);The second form is worth calling out: instead of an index, the path can start with a function that filters which array entries the update applies to. This avoids manually searching for an index first, and it can update multiple matching entries in one call.
produce: mutate-looking syntax, immutable underneath
For updates that touch several fields at once, writing out separate path calls gets repetitive. produce (inspired by Immer, which React developers may already know) lets you write code that looks like direct mutation, while Solid still applies it as a precise, trackable update:
import { produce } from "solid-js/store";
setTodos(
produce((todos) => {
todos[0].done = true;
todos[1].text = "Build a Solid app";
})
);Inside produce's callback, todos is a temporary draft you can assign into directly with =. This is purely an ergonomic convenience — under the hood, Solid still applies these as fine-grained updates to the underlying store, not a wholesale replacement. Reach for produce when a single logical update needs to touch multiple paths at once; for a single field, the plain path setter (setTodos(0, "done", true)) is simpler and just as precise.
reconcile: merging in new data wholesale
When you fetch fresh data from an API and need to replace a store's contents, a naive setStore(newData) throws away all the fine-grained identity Solid was tracking — every consumer re-evaluates as if everything changed, even fields that happen to have the same value. reconcile diffs the new data against the existing store and applies only the actual differences:
import { reconcile } from "solid-js/store";
async function refreshTodos() {
const fresh = await fetch("/api/todos").then((r) => r.json());
setTodos(reconcile(fresh));
}If nine out of ten todos in fresh are identical to what's already in the store, reconcile leaves those nine untouched and updates only the one that actually changed — preserving fine-grained reactivity across a full data replacement instead of blowing it away.
Choosing the right tool
- A single known field → a plain path setter:
setStore("path", "to", "field", value). - Several related fields in one logical update →
produce. - Replacing the store's contents with freshly fetched data →
reconcile.
All three ultimately produce the same kind of precise, fine-grained update — they just differ in how convenient each is for the shape of change you're making. Defaulting to path setters for simple cases and reaching for produce/reconcile only when the update genuinely calls for them keeps store code both readable and fast.