diff --git a/.changeset/fix-projection-refresh-pending.md b/.changeset/fix-projection-refresh-pending.md new file mode 100644 index 000000000..c5e649d48 --- /dev/null +++ b/.changeset/fix-projection-refresh-pending.md @@ -0,0 +1,5 @@ +--- +"@solidjs/signals": patch +--- + +Fix projection pending state on first refresh after initial async resolution. diff --git a/packages/solid-signals/src/core/async.ts b/packages/solid-signals/src/core/async.ts index 54770abda..b2f0c9da2 100644 --- a/packages/solid-signals/src/core/async.ts +++ b/packages/solid-signals/src/core/async.ts @@ -197,8 +197,10 @@ export function handleAsync( clearStatus(el); const lane = resolveLane(el as any); if (lane) lane._pendingAsync.delete(el); - if (setter) setter(value); - else if (el._overrideValue !== undefined) { + if (setter) { + setter(value); + if (wasUninitialized) clearStatus(el, true); + } else if (el._overrideValue !== undefined) { if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING) el._pendingValue = value; else { diff --git a/packages/solid-signals/tests/createLoadingBoundary.test.ts b/packages/solid-signals/tests/createLoadingBoundary.test.ts index aec48622d..8740f86b7 100644 --- a/packages/solid-signals/tests/createLoadingBoundary.test.ts +++ b/packages/solid-signals/tests/createLoadingBoundary.test.ts @@ -11,6 +11,7 @@ import { isPending, latest, NotReadyError, + type Refreshable, type SourceAccessor, refresh, untrack @@ -93,6 +94,44 @@ describe("createLoadingBoundary", () => { expect(result).toBe(0); // Should show 0, not "loading" }); + it("reports pending on first refresh after an async projection resolves", async () => { + let result: any; + let projection!: Refreshable<{ value: number }>; + let current = deferred<{ value: number }>(); + + createRoot(() => { + projection = createProjection(async () => current.promise, {} as { value: number }); + + const boundary = createLoadingBoundary( + () => [projection.value, isPending(() => projection.value)], + () => "loading" + ); + + createRenderEffect( + () => (result = boundary()), + () => {} + ); + }); + + flush(); + expect(result).toBe("loading"); + + current.resolve({ value: 1 }); + await new Promise(resolve => setTimeout(resolve, 0)); + flush(); + expect(result).toEqual([1, false]); + + current = deferred<{ value: number }>(); + refresh(projection); + flush(); + expect(result).toEqual([1, true]); + + current.resolve({ value: 2 }); + await new Promise(resolve => setTimeout(resolve, 0)); + flush(); + expect(result).toEqual([2, false]); + }); + it("clears loading for multiple effects in same boundary", async () => { let result1: any, result2: any;