Skip to content

Commit 11dce5d

Browse files
JerryWu1234wuls
andauthored
fix: retry computed qrl execution when not ready (#7470)
* feat(qrl): add QRL component and e2e test for inner computed QRL Introduce a new QRL component to handle inner computed QRL logic and add an e2e test to verify its behavior. The test ensures that the counter updates correctly without uncaught promises. Additionally, update the QRL class to use `retryOnPromise` for better error handling during QRL resolution. * refactor(e2e): simplify test structure by removing unnecessary describe block The describe block was removed to streamline the test file and improve readability. The test logic remains unchanged, focusing on verifying the counter update functionality without uncaught promises. * test(e2e): mark counter update test as .only for focused execution refactor(qrl): simplify promise handling in QRL class by removing redundant retryOnPromise * fix test fail * feat(qrl): add QRL component and e2e test for inner computed QRL Introduce a new QRL component to handle inner computed QRL logic and add an e2e test to verify its behavior. The test ensures that the counter updates correctly without uncaught promises. Additionally, update the QRL class to use `retryOnPromise` for better error handling during QRL resolution. * refactor(e2e): simplify test structure by removing unnecessary describe block The describe block was removed to streamline the test file and improve readability. The test logic remains unchanged, focusing on verifying the counter update functionality without uncaught promises. * test(e2e): mark counter update test as .only for focused execution refactor(qrl): simplify promise handling in QRL class by removing redundant retryOnPromise * fix test fail * add change * fix information * fix ts error --------- Co-authored-by: wuls <[email protected]>
1 parent 56ed5bd commit 11dce5d

File tree

5 files changed

+56
-2
lines changed

5 files changed

+56
-2
lines changed

.changeset/sweet-hairs-remember.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': patch
3+
---
4+
5+
FIX: Introduce retry logic for QRL resolution to handle potential promise retries, ensuring robustness in asynchronous operations.

packages/qwik/src/core/shared/qrl/qrl-class.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
type InvokeTuple,
1313
} from '../../use/use-core';
1414
import { getQFuncs, QInstanceAttr } from '../utils/markers';
15-
import { isPromise, maybeThen } from '../utils/promises';
15+
import { isPromise, maybeThen, retryOnPromise } from '../utils/promises';
1616
import { qDev, qSerialize, qTest, seal } from '../utils/qdev';
1717
import { isArray, isFunction, type ValueOrPromise } from '../utils/types';
1818
import type { QRLDev } from './qrl';
@@ -93,7 +93,8 @@ export const createQRL = <TYPE>(
9393
// Note that we bind the current `this`
9494
const bound = (...args: QrlArgs<TYPE>): ValueOrPromise<QrlReturn<TYPE> | undefined> => {
9595
if (!qrl.resolved) {
96-
return qrl.resolve().then((fn) => {
96+
const promise = retryOnPromise(() => qrl.resolve()) as Promise<TYPE>;
97+
return promise.then((fn) => {
9798
if (!isFunction(fn)) {
9899
throw qError(QError.qrlIsNotFunction);
99100
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { $, component$, useComputed$, useSignal } from "@qwik.dev/core";
2+
3+
export const QRL = component$(() => {
4+
return (
5+
<>
6+
<ShouldResolveInnerComputedQRL />
7+
</>
8+
);
9+
});
10+
11+
export const ShouldResolveInnerComputedQRL = component$(() => {
12+
const test = useComputed$(() => 0);
13+
return <InnerComputedButton test={test} />;
14+
});
15+
16+
export const InnerComputedButton = component$<any>((props) => {
17+
const syncSelectionCounter = useSignal(0);
18+
const syncSelection = $(() => {
19+
syncSelectionCounter.value++;
20+
props.test.value;
21+
});
22+
23+
const handleClick = $(() => {
24+
syncSelection();
25+
});
26+
27+
return (
28+
<button id="inner-computed-button" onClick$={handleClick}>
29+
Click Me {syncSelectionCounter.value}
30+
</button>
31+
);
32+
});

starters/apps/e2e/src/root.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { UseId } from "./components/useid/useid";
3535
import { Watch } from "./components/watch/watch";
3636

3737
import "./global.css";
38+
import { QRL } from "./components/qrl/qrl";
3839

3940
const tests: Record<string, FunctionComponent> = {
4041
"/e2e/two-listeners": () => <TwoListeners />,
@@ -71,6 +72,7 @@ const tests: Record<string, FunctionComponent> = {
7172
"/e2e/build-variables": () => <BuildVariables />,
7273
"/e2e/exception/render": () => <RenderExceptions />,
7374
"/e2e/exception/use-task": () => <UseTaskExceptions />,
75+
"/e2e/qrl": () => <QRL />,
7476
};
7577

7678
export const Root = component$<{ pathname: string }>(({ pathname }) => {

starters/e2e/e2e.qrl.e2e.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { expect, test } from "@playwright/test";
2+
test("should update counter without uncaught promises", async ({ page }) => {
3+
await page.goto("/e2e/qrl");
4+
page.on("pageerror", (err) => expect(err).toEqual(undefined));
5+
page.on("console", (msg) => {
6+
if (msg.type() === "error") {
7+
expect(msg.text()).toEqual(undefined);
8+
}
9+
});
10+
const button = page.locator("#inner-computed-button");
11+
await expect(button).toContainText("Click Me 0");
12+
13+
await button.click();
14+
});

0 commit comments

Comments
 (0)