From 22bd4969ba98f65ce53cb4b8eb53f20b187c7078 Mon Sep 17 00:00:00 2001 From: "Jacques P. du Toit" Date: Wed, 11 Mar 2026 21:50:00 +0100 Subject: [PATCH 1/2] Set committed value for computations created during transition Fixes #2046 --- packages/solid/src/reactive/signal.ts | 2 + packages/solid/web/test/transition.spec.tsx | 72 +++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 packages/solid/web/test/transition.spec.tsx diff --git a/packages/solid/src/reactive/signal.ts b/packages/solid/src/reactive/signal.ts index ddce76160..97caeaa4c 100644 --- a/packages/solid/src/reactive/signal.ts +++ b/packages/solid/src/reactive/signal.ts @@ -1417,6 +1417,8 @@ function runComputation(node: Computation, value: any, time: number) { if (node.updatedAt != null && "observers" in node) { writeSignal(node as Memo, nextValue, true); } else if (Transition && Transition.running && node.pure) { + // On first computation during transition, also set committed value #2046 + if (!Transition.sources.has(node as Memo)) node.value = nextValue; Transition.sources.add(node as Memo); (node as Memo).tValue = nextValue; } else node.value = nextValue; diff --git a/packages/solid/web/test/transition.spec.tsx b/packages/solid/web/test/transition.spec.tsx new file mode 100644 index 000000000..064aa4eaa --- /dev/null +++ b/packages/solid/web/test/transition.spec.tsx @@ -0,0 +1,72 @@ +/** + * @jsxImportSource solid-js + * @vitest-environment jsdom + */ + +import { describe, expect, test } from "vitest"; +import { createSignal, createMemo, createResource, useTransition } from "../../src/index.js"; +import { render, Suspense } from "../src/index.js"; + +describe("Transition memo stale read (#2046)", () => { + test("memo created during transition should not return undefined in committed state", async () => { + const div = document.createElement("div"); + const [route, setRoute] = createSignal("home"); + const [dbVersion, setDbVersion] = createSignal(1); + const [pending, start] = useTransition(); + let dataRef: (() => { q: number }) | null = null; + let resolveResource: (v: string) => void; + + function RouteComponent() { + // Always returns {q:42}. Never undefined. + const data = createMemo(() => ({ q: 42 })); + // Reads both dbVersion (external signal) and data + const label = createMemo(() => dbVersion() + ": " + data()!.q); + dataRef = data; + return

{label()}

; + } + + let fetchCount = 0; + const dispose = render(() => { + const [resource] = createResource( + () => route(), + r => { + fetchCount++; + // First fetch resolves immediately + if (fetchCount <= 1) return Promise.resolve(r); + // Second fetch (during transition) stays pending + return new Promise(resolve => { + resolveResource = resolve; + }); + } + ); + return ( + +

{resource()}

+ {route() === "detail" && } +
+ ); + }, div); + + // Wait for initial resource to resolve + await Promise.resolve(); + await Promise.resolve(); + + // Navigate via transition — resource refetches, keeps transition pending + start(() => setRoute("detail")); + await Promise.resolve(); + await Promise.resolve(); + await Promise.resolve(); + + // RouteComponent mounted during transition, transition is pending + expect(dataRef).not.toBeNull(); + expect(pending()).toBe(true); + + // External signal change while transition is pending. + // label recomputes → reads data() → should be {q:42}, not undefined. + setDbVersion(2); + expect(dataRef!()).toEqual({ q: 42 }); + + resolveResource!("done"); + dispose(); + }); +}); From a33416386db0a3bf34c7b06b69dafa7bb6458b57 Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Tue, 24 Mar 2026 11:58:58 -0700 Subject: [PATCH 2/2] Add changeset for solid-js patch Set committed value for computations created during transition --- .changeset/heavy-grapes-sin.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/heavy-grapes-sin.md diff --git a/.changeset/heavy-grapes-sin.md b/.changeset/heavy-grapes-sin.md new file mode 100644 index 000000000..516222c31 --- /dev/null +++ b/.changeset/heavy-grapes-sin.md @@ -0,0 +1,5 @@ +--- +"solid-js": patch +--- + +Set committed value for computations created during transition