You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: website/docs/ARCHITECTURE.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -319,7 +319,7 @@ flowchart TD
319
319
4. For getters reading other getters (e.g., `get filteredCount() { return this.filtered.length; }`): the inner getter is itself memoized, so it returns a stable reference, which the outer getter's dep check recognizes as unchanged.
320
320
321
321
**Impact on `useStore`:** The existing hook benefits automatically — no changes needed:
322
-
-**Selector mode:**`useStore(store, s => s.filtered)` gets a stable reference from the memoized snapshot getter. `Object.is` correctly detects "no change" without `shallowEqual`.
322
+
-**Selector mode:**`useStore(store, (store) => store.filtered)` gets a stable reference from the memoized snapshot getter. `Object.is` correctly detects "no change" without `shallowEqual`.
The selector receives an immutable snapshot and returns the slice you need. The component re-renders only when the selected value changes (compared with `Object.is`).
@@ -111,8 +111,8 @@ By default, `useStore` compares the selector's return value with `Object.is`, wh
111
111
112
112
```ts
113
113
// ✅ No shallowEqual needed — structural sharing keeps the reference stable
114
-
const count =useStore(todoStore, (s) =>s.items.length);
const todos =useStore(todoStore, (store) =>store.items);
116
116
```
117
117
118
118
The problem shows up when your selector **derives a new value** — calling `.filter()`, `.map()`, or using object spread always allocates a new array or object, even when the underlying data hasn't changed. `Object.is` sees a different reference and triggers a re-render.
@@ -125,7 +125,7 @@ Without `shallowEqual` — `.filter()` creates a new array every time the snapsh
`shallowEqual` is only needed when the **selector itself** derives a new value (via `.filter()`, `.map()`, object spread, etc.) — not when selecting a getter that does the derivation internally.
While modifying nested properties directly through the proxy works, the same best practice applies here: prefer store methods over inline mutations. When nested updates are scattered across components, it becomes difficult to trace how deeply nested state changes. Methods give you a single place to look.
252
252
253
-
Structural sharing means that when you mutate `metadata.title`, the snapshot for `sections` is reused from the previous snapshot (same reference). A component selecting `s => s.sections` won't re-render because its selected value hasn't changed.
253
+
Structural sharing means that when you mutate `metadata.title`, the snapshot for `sections` is reused from the previous snapshot (same reference). A component selecting `(store) => store.sections` won't re-render because its selected value hasn't changed.
254
254
255
255
## Collections
256
256
@@ -365,7 +365,7 @@ class PostStore {
365
365
}
366
366
```
367
367
368
-
This means a component using `useStore(postStore, s => s.loading)` will re-render twice: once when loading starts, once when it ends. That's the correct behavior.
368
+
This means a component using `useStore(postStore, (store) => store.loading)` will re-render twice: once when loading starts, once when it ends. That's the correct behavior.
369
369
370
370
## Tips & Gotchas
371
371
@@ -416,7 +416,7 @@ class Counter {
416
416
}
417
417
418
418
// ✅ Selector mode: no re-render (Object.is sees same count value)
419
-
const count =useStore(counterStore, (s) =>s.count);
0 commit comments