rc.14: inside an action, a non-keyed `<Show>`'s child memo re-runs after its condition turned falsy, and the stale-value throw halts reactivity
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 47/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- javascript, typescript
Direzione di ricerca
Start by running the linked solid-flow reproduction with bunx vitest run src/core/__tests__/showStaleInAction.test.ts; compare its rc.15 failure with the rc.13 result described in REPRO.md. Trace the update ordering around the non-keyed <Show> branch and its child memo in the reproduction. Done means the child is disposed before it re-runs after when becomes undefined, and the regression test passes without halting reactivity.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
solid-js / @solidjs/[email protected] and rc.15. Regression since rc.13.
Inside an open action, a non-keyed <Show when={lookup[id]}> mounts its branch for a row the action added, and later in the same transaction its condition becomes undefined while the <For> that owns it still lists the id. The engine then re-runs a memo inside the branch (one that reads the Show's narrowed accessor) instead of disposing it first. The read throws "Attempting to access a stale value from that could possibly be undefined", and since that memo has no error boundary the whole system stops: REACTIVITY_HALTED, and no further updates are processed. On rc.13 the same sequence never throws (our DOM test asserts the added row is rendered and selected mid-action).
Traced sequence in the failing case (b is the row the action adds):
| step | <For> ids |
<Show when> for b |
b's branch memo |
|---|---|---|---|
| action adds b, selects it | ["a","b"] |
the row | mounts, runs |
| same transaction, later pass | ["a","b"] |
undefined |
RE-RUNS, throws the stale read |
Two things changed between rc.13 and rc.14 here. A derived keyed record drops a row the action added while the id list still has it; that may be an intended effect of rc.14's held writes (the plain-store writes inside the action are now held with the transaction). The re-run of a child under a falsy non-keyed <Show> is the part that looks wrong regardless: the branch should be disposed before its children run again, and a stale read in a child is turned into a crash of the whole graph.
Reproduction (headless, no DOM, no components)
Not yet reduced to @solidjs/signals alone. Inside the select step, each of its four operations is needed (removing any one avoids the crash: the index-memo read, the selected field write, the nested overlay write, the Object.assign). The surrounding pieces are a plain store over a controlled createOptimisticStore (the flow's own node list), an id list subscribed through Reflect.ownKeys, a membership-cadence index memo read untracked inside a setter, per-row createProjections (keyed mapArray) joined with a plain selection-overlay store, a keyed record projection of those rows, and an action that adds a row, then in a second setter field-writes selected on it, nests a write of the overlay entry (holding the row proxy), and Object.assigns the patch. A signals-only model with those pieces does not reproduce yet: there the added row is visible mid-action, where in the library it is held.
The repro uses solid-flow's headless flow state, rendered through solid-js <For> / <Show> with one eager memo per row. Branch repro/solid-rc14-show-stale (commit ec5977d): solid-flow next at addaf7a with the Solid packages at 2.0.0-rc.15, plus the test and a REPRO.md. Setting the Solid packages back to 2.0.0-rc.13 (steps in REPRO.md) makes the same test pass.
git clone -b repro/solid-rc14-show-stale https://github.com/dsnchz/solid-flow && cd solid-flow
bun install
bunx vitest run src/core/__tests__/showStaleInAction.test.ts # fails on rc.15: REACTIVITY_HALTED
const [nodes] = createOptimisticStore<Node[]>([node("a", 0)]);
createRoot(() => {
state = createFlowState({ nodes, edges: [] });
const view = For({
get each() { return state.store.visibleNodeIds; },
children: (id) => Show({
get when() { return state.nodeLookup.get(id); },
children: (row) => {
const label = createMemo(() => `${row().id}:${row().selected}`); // re-runs after `when` went falsy
return () => label();
},
}),
});
createRenderEffect(() => JSON.stringify(resolve(view)), () => {});
});
flush();
const addAndSelect = action(function* () {
state.commands.addNodes(node("b", 200));
state.commands.updateNode("b", { selected: true });
yield gate;
});
solid-js / @solidjs/signals |
result |
|---|---|
| 2.0.0-rc.13 | renders; no error |
| 2.0.0-rc.14 | REACTIVITY_HALTED (stale <Show> read in the row memo) |
| 2.0.0-rc.15 | REACTIVITY_HALTED (stale <Show> read in the row memo) |
On rc.15, without the updateNode call (add only), or selecting an existing row, or updating the new row's data instead of selected, nothing throws.
Where we hit it
@dschz/solid-flow composes flow commands with createOptimisticStore + action (the composition from #3085; our pin for #3665 covers this same add-then-select-in-an-action scenario). In the real component, NodeWrapper's culling memo reads the row through the Show's accessor, so a user action that adds a node and selects it (addNodes then updateNode(id, { selected: true })) before its yield freezes the whole flow on rc.14 and rc.15. We are holding our rc.15 bump on it.
- Lingua principale
- TypeScript
- Stelle
- 36.1k
- Fork
- 1.1k
- Merge medio
- 11h 42m
- PR unite (30g)
- 350
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di solidjs/solid
-
`lazy()`: remounting an already-loaded component renders empty (or the Suspense fallback) since 1.9.14Forse già presa @itisrmk l’ha presa 4 giorni fa. Aperta1.x
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 1 giorno
-
[2.0] Effects re-run (and run cleanup) when the compute returns an equal value; there's no `equals` optionForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 54/100
I maintainer di solito rispondono entro 1 giorno
-
[2.0] The MIGRATION.md replacement for `from()` keeps the upstream subscription after dispose; add a subscription helperForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 56/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di solidjs/solid
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
paperclipai/paperclip#17151 ·
I maintainer di solito rispondono entro 1 giorno
-
needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 83/100
AgoraIO-Extensions/agent-infra#3159 ·
I maintainer di solito rispondono entro 1 giorno
-
blocklist addition
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 66/100
MetaMask/eth-phishing-detect#304596 ·
I maintainer di solito rispondono entro 1 giorno
-
friction
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
kentcdodds/kody#4142 ·
-
[Bug] Statistics | Task statistics are always empty on MikroORM with MySQL or SQLiteForse già presa @NdekoCode l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
ever-co/ever-gauzy#11214 ·
I maintainer di solito rispondono entro 1 giorno