diff --git a/packages/qwik/src/core/shared/shared-serialization.ts b/packages/qwik/src/core/shared/shared-serialization.ts index d52c5db28c5..cdf2a64b6f0 100644 --- a/packages/qwik/src/core/shared/shared-serialization.ts +++ b/packages/qwik/src/core/shared/shared-serialization.ts @@ -42,6 +42,7 @@ import { type SyncQRLInternal, } from './qrl/qrl-class'; import type { QRL } from './qrl/qrl.public'; +import { ChoreType } from './scheduler'; import type { DeserializeContainer, HostElement, ObjToProxyMap } from './types'; import { _CONST_PROPS, _VAR_PROPS } from './utils/constants'; import { isElement, isNode } from './utils/element'; @@ -282,6 +283,15 @@ const inflate = (container: DeserializeContainer, target: any, typeId: TypeIds, computed.$untrackedValue$ = d[1]; computed.$invalid$ = d[2]; computed.$effects$ = d.slice(3); + /** + * If we try to compute value and the qrl is not resolved, then system throws an error with + * qrl promise. To prevent that we should early resolve computed qrl while computed + * deserialization. This also prevents anything from firing while computed qrls load, because + * of scheduler + */ + // try to download qrl in this tick + computed.$computeQrl$.resolve(); + (container as DomContainer).$scheduler$?.(ChoreType.QRL_RESOLVE, null, computed.$computeQrl$); break; } case TypeIds.Error: { diff --git a/starters/apps/e2e/src/components/computed/computed.tsx b/starters/apps/e2e/src/components/computed/computed.tsx index 6baa712e178..afc07db9464 100644 --- a/starters/apps/e2e/src/components/computed/computed.tsx +++ b/starters/apps/e2e/src/components/computed/computed.tsx @@ -18,6 +18,7 @@ export const ComputedRoot = component$(() => { + ); }); @@ -107,3 +108,32 @@ export const Issue5738 = component$(() => { }); return
Calc: {comp.value}
; }); + +export const ShouldResolveComputedQrlEarly = component$(() => { + const isToggled = useSignal(false); + + const demo = useComputed$(() => 3); + + // change attribute and read computed + const repro = useComputed$(() => { + if (!isToggled.value) { + return; + } + + // happens when we read another computed value + return demo.value + 2; + }); + + return ( + <> + + + ); +}); diff --git a/starters/e2e/e2e.computed.e2e.ts b/starters/e2e/e2e.computed.e2e.ts index 35b196cc7cd..27f5d12e997 100644 --- a/starters/e2e/e2e.computed.e2e.ts +++ b/starters/e2e/e2e.computed.e2e.ts @@ -33,6 +33,17 @@ test.describe("computed", () => { ]); }); + test("should early resolve computed qrl", async ({ page }) => { + const button = page.locator("#early-computed-qrl"); + await expect(button).not.toHaveAttribute("data-test"); + await expect(button).toContainText("Click me!"); + + await button.click(); + + await expect(button).toHaveAttribute("data-test", "5"); + await expect(button).toContainText("Click me! 5"); + }); + test("issue 3482", async ({ page }) => { const button = page.locator("#issue-3482-button"); const div = page.locator("#issue-3482-div");