Skip to content

Commit 311ab2b

Browse files
nyanrusXiNiHa
andauthored
fix: preserve fragment data identity across snapshot updates (#68)
* fix: preserve fragment data identity across snapshot updates `reconcile({ key: "__id", merge: true })` only preserves identity by walking the existing tree in place when the current store value is wrappable. solid-js/store's `reconcile` (modifiers.ts) early-returns the new value as-is when it isn't, so reconciling against `undefined` silently produces a fresh top-level reference and the merge contract goes away. The result observer was pre-clearing `data` to `undefined` inside the same batch that subsequently applied the reconciled snapshot, so that contract was being defeated on every Relay update. Identity-sensitive consumers — most prominently `<Show keyed when={data()}>` — re-mounted their subtree on every snapshot tick, including pure field updates such as a reaction toggle or a polled count delta. In a real fediverse timeline (hackers.pub `/feed`, ~25 cards × multiple Kobalte primitives per card), this dominated CPU and heap during interaction (~+10 MB/s sustained vs. ~0 MB/s when idle on the same route). Restructure the observer so each branch owns its full state transition and no shared pre-clears defeat the reconcile: - "ok" — clear `error`, set `pending` to false, reconcile `data`. - "error" — set `data` to `undefined`, set `error`, set `pending`. - "loading" — no case. Leave `data` / `error` / `pending` as-is for stale-while-revalidate behaviour, matching `createLazyLoadQuery`'s observer in the same package. Behaviour is otherwise unchanged. Observers never saw the intermediate pre-clear state — every mutation is inside the same `batch()` — so the fix is invisible to anything except identity comparisons across snapshot boundaries, which the reconcile contract is exactly there to keep stable. Assisted-by: Claude Code:claude-opus-4-7 * chore: clean up comments * fix: handle loading fragment state properly * chore: add changeset --------- Co-authored-by: Iha Shin <me@xiniha.dev>
1 parent 1bfb1a9 commit 311ab2b

2 files changed

Lines changed: 10 additions & 3 deletions

File tree

.changeset/common-clocks-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"solid-relay": patch
3+
---
4+
5+
fix: preserve fragment data identity across snapshot updates

src/primitives/createFragment.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,6 @@ export function createFragmentInternal<
132132
next(res) {
133133
queueMicrotask(() => {
134134
batch(() => {
135-
setResult("data", undefined);
136-
setResult("error", undefined);
137-
138135
switch (res.state) {
139136
case "ok":
140137
setResult("error", undefined);
@@ -146,6 +143,11 @@ export function createFragmentInternal<
146143
setResult("error", res.error);
147144
setResult("pending", false);
148145
break;
146+
case "loading":
147+
setResult("data", undefined);
148+
setResult("error", undefined);
149+
setResult("pending", true);
150+
break;
149151
}
150152
});
151153
});

0 commit comments

Comments
 (0)