Skip to content

Commit e33abbb

Browse files
JerryWu1234wulsgioboa
authored
fix: fix a resource a type error when using async _resolved function (#7426)
* fix: fix a resource a type error when using async _resolved function * Create tidy-snakes-flow.md * fix test * Delete .changeset/tidy-snakes-flow.md * fix * add change * add promise for pending * refactor(resource): allow async callbacks for resource lifecycle handlers * fix test * fix test * fix test * fix test * fix test * fix test * delete only * chore: update api files 🍉 * chore: change changeset message 🍒 --------- Co-authored-by: wuls <[email protected]> Co-authored-by: Giorgio Boa <[email protected]> Co-authored-by: gioboa <[email protected]>
1 parent 1719007 commit e33abbb

File tree

6 files changed

+89
-31
lines changed

6 files changed

+89
-31
lines changed

.changeset/polite-singers-sing.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@builder.io/qwik-city': patch
3+
'@builder.io/qwik': patch
4+
---
5+
6+
FIX: solve type error when using async _resolved function

packages/docs/src/routes/api/qwik/api.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@
17741774
}
17751775
],
17761776
"kind": "Function",
1777-
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n\n**Returns:**\n\n[JSXNode](#jsxnode)<!-- -->&lt;'script'&gt;",
1777+
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n\n**Returns:**\n\nJSXNode&lt;'script'&gt;",
17781778
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts",
17791779
"mdFile": "qwik.prefetchserviceworker.md"
17801780
},
@@ -2432,7 +2432,7 @@
24322432
}
24332433
],
24342434
"kind": "Interface",
2435-
"content": "```typescript\nexport interface ResourceProps<T> \n```\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[onPending?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n() =&gt; [JSXOutput](#jsxoutput)\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[onRejected?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n(reason: Error) =&gt; [JSXOutput](#jsxoutput)\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[onResolved](#)\n\n\n</td><td>\n\n\n</td><td>\n\n(value: T) =&gt; [JSXOutput](#jsxoutput)\n\n\n</td><td>\n\n\n</td></tr>\n<tr><td>\n\n[value](#)\n\n\n</td><td>\n\n`readonly`\n\n\n</td><td>\n\n[ResourceReturn](#resourcereturn)<!-- -->&lt;T&gt; \\| [Signal](#signal)<!-- -->&lt;Promise&lt;T&gt; \\| T&gt; \\| Promise&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>",
2435+
"content": "```typescript\nexport interface ResourceProps<T> \n```\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[onPending?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n() =&gt; [JSXOutput](#jsxoutput) \\| Promise&lt;[JSXOutput](#jsxoutput)<!-- -->&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[onRejected?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n(reason: Error) =&gt; [JSXOutput](#jsxoutput) \\| Promise&lt;[JSXOutput](#jsxoutput)<!-- -->&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[onResolved](#)\n\n\n</td><td>\n\n\n</td><td>\n\n(value: T) =&gt; [JSXOutput](#jsxoutput) \\| Promise&lt;[JSXOutput](#jsxoutput)<!-- -->&gt;\n\n\n</td><td>\n\n\n</td></tr>\n<tr><td>\n\n[value](#)\n\n\n</td><td>\n\n`readonly`\n\n\n</td><td>\n\n[ResourceReturn](#resourcereturn)<!-- -->&lt;T&gt; \\| [Signal](#signal)<!-- -->&lt;Promise&lt;T&gt; \\| T&gt; \\| Promise&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>",
24362436
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-resource.ts",
24372437
"mdFile": "qwik.resourceprops.md"
24382438
},

packages/docs/src/routes/api/qwik/index.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3667,7 +3667,7 @@ opts
36673667
36683668
**Returns:**
36693669
3670-
[JSXNode](#jsxnode)&lt;'script'&gt;
3670+
JSXNode&lt;'script'&gt;
36713671
36723672
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts)
36733673
@@ -5081,7 +5081,7 @@ Description
50815081
50825082
</td><td>
50835083
5084-
() =&gt; [JSXOutput](#jsxoutput)
5084+
() =&gt; [JSXOutput](#jsxoutput) \| Promise&lt;[JSXOutput](#jsxoutput)&gt;
50855085
50865086
</td><td>
50875087
@@ -5096,7 +5096,7 @@ _(Optional)_
50965096
50975097
</td><td>
50985098
5099-
(reason: Error) =&gt; [JSXOutput](#jsxoutput)
5099+
(reason: Error) =&gt; [JSXOutput](#jsxoutput) \| Promise&lt;[JSXOutput](#jsxoutput)&gt;
51005100
51015101
</td><td>
51025102
@@ -5111,7 +5111,7 @@ _(Optional)_
51115111
51125112
</td><td>
51135113
5114-
(value: T) =&gt; [JSXOutput](#jsxoutput)
5114+
(value: T) =&gt; [JSXOutput](#jsxoutput) \| Promise&lt;[JSXOutput](#jsxoutput)&gt;
51155115
51165116
</td><td>
51175117

packages/qwik/src/core/qwik.core.api.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -886,11 +886,11 @@ export interface ResourcePending<T> {
886886
// @public (undocumented)
887887
export interface ResourceProps<T> {
888888
// (undocumented)
889-
onPending?: () => JSXOutput;
889+
onPending?: () => JSXOutput | Promise<JSXOutput>;
890890
// (undocumented)
891-
onRejected?: (reason: Error) => JSXOutput;
891+
onRejected?: (reason: Error) => JSXOutput | Promise<JSXOutput>;
892892
// (undocumented)
893-
onResolved: (value: T) => JSXOutput;
893+
onResolved: (value: T) => JSXOutput | Promise<JSXOutput>;
894894
// (undocumented)
895895
readonly value: ResourceReturn<T> | Signal<Promise<T> | T> | Promise<T>;
896896
}

packages/qwik/src/core/render/ssr/render-ssr.unit.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,25 @@ test('issue 4283', async () => {
15501550
);
15511551
});
15521552

1553+
test('AsyncResource', async () => {
1554+
await testSSR(
1555+
<body>
1556+
<ul>
1557+
<AsyncResource text="thing" delay={500} />
1558+
</ul>
1559+
</body>,
1560+
`<html q:container="paused" q:version="dev" q:render="ssr-dev" q:base="" q:manifest-hash="test">
1561+
<body>
1562+
<ul>
1563+
<!--qv q:id=0 q:key=sX:-->
1564+
<div class="cmp1"><!--qkssr-f--><span>1</span>;</div>
1565+
<!--/qv-->
1566+
</ul>
1567+
</body>
1568+
</html>`
1569+
);
1570+
});
1571+
15531572
// TODO
15541573
// Merge props on host
15551574
// - host events
@@ -1882,6 +1901,25 @@ export const DelayResource = component$((props: { text: string; delay: number })
18821901
);
18831902
});
18841903

1904+
export const AsyncResource = component$((props: { text: string; delay: number }) => {
1905+
const resource = useResource$<string>(async ({ track }) => {
1906+
track(() => props.text);
1907+
await delay(props.delay);
1908+
return props.text;
1909+
});
1910+
return (
1911+
<div class="cmp1">
1912+
<Resource
1913+
value={resource}
1914+
onResolved={async () => <span>1</span>}
1915+
onRejected={async () => <span>1</span>}
1916+
onPending={async () => <span>1</span>}
1917+
/>
1918+
;
1919+
</div>
1920+
);
1921+
});
1922+
18851923
export const NullCmp = component$(() => {
18861924
return null;
18871925
});

packages/qwik/src/core/use/use-resource.ts

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ export const useResource$ = <T>(
191191
/** @public */
192192
export interface ResourceProps<T> {
193193
readonly value: ResourceReturn<T> | Signal<Promise<T> | T> | Promise<T>;
194-
onResolved: (value: T) => JSXOutput;
195-
onPending?: () => JSXOutput;
196-
onRejected?: (reason: Error) => JSXOutput;
194+
onResolved: (value: T) => JSXOutput | Promise<JSXOutput>;
195+
onPending?: () => JSXOutput | Promise<JSXOutput>;
196+
onRejected?: (reason: Error) => JSXOutput | Promise<JSXOutput>;
197197
}
198198

199199
// <docs markdown="../readme.md#useResource">
@@ -252,48 +252,62 @@ export interface ResourceProps<T> {
252252
*/
253253
// </docs>
254254
export const Resource = <T>(props: ResourceProps<T>): JSXOutput => {
255-
const isBrowser = !isServerPlatform();
255+
// Resource path
256+
return jsx(Fragment, {
257+
children: getResourceValueAsPromise(props),
258+
});
259+
};
260+
function getResourceValueAsPromise<T>(props: ResourceProps<T>): Promise<JSXOutput> | JSXOutput {
256261
const resource = props.value as ResourceReturnInternal<T> | Promise<T> | Signal<T>;
257-
let promise: Promise<T> | undefined;
258262
if (isResourceReturn(resource)) {
263+
const isBrowser = !isServerPlatform();
259264
if (isBrowser) {
260265
if (props.onRejected) {
261-
resource.value.catch(() => {});
262266
if (resource._state === 'rejected') {
263-
return props.onRejected(resource._error!);
267+
return Promise.resolve(resource._error!).then(useBindInvokeContext(props.onRejected));
264268
}
265269
}
266270
if (props.onPending) {
267271
const state = resource._state;
268272
if (state === 'resolved') {
269-
return props.onResolved(resource._resolved!);
273+
return Promise.resolve(resource._resolved!).then(useBindInvokeContext(props.onResolved));
270274
} else if (state === 'pending') {
271-
return props.onPending();
275+
return Promise.resolve().then(useBindInvokeContext(props.onPending));
272276
} else if (state === 'rejected') {
273277
throw resource._error;
274278
}
275279
}
276280
if (untrack(() => resource._resolved) !== undefined) {
277-
return props.onResolved(resource._resolved!);
281+
return Promise.resolve(resource._resolved!).then(useBindInvokeContext(props.onResolved));
278282
}
279283
}
280-
promise = resource.value;
284+
const value = resource.value;
285+
if (value) {
286+
return value.then(
287+
useBindInvokeContext(props.onResolved),
288+
useBindInvokeContext(props.onRejected)
289+
);
290+
} else {
291+
// this is temporary value until the `runResource` is executed and promise is assigned to the value
292+
return Promise.resolve(undefined);
293+
}
281294
} else if (isPromise(resource)) {
282-
promise = resource;
295+
return resource.then(
296+
useBindInvokeContext(props.onResolved),
297+
useBindInvokeContext(props.onRejected)
298+
);
283299
} else if (isSignal(resource)) {
284-
promise = Promise.resolve(resource.value);
300+
return Promise.resolve(resource.value).then(
301+
useBindInvokeContext(props.onResolved),
302+
useBindInvokeContext(props.onRejected)
303+
);
285304
} else {
286-
return props.onResolved(resource as T);
287-
}
288-
289-
// Resource path
290-
return jsx(Fragment, {
291-
children: promise.then(
305+
return Promise.resolve(resource as T).then(
292306
useBindInvokeContext(props.onResolved),
293307
useBindInvokeContext(props.onRejected)
294-
),
295-
});
296-
};
308+
);
309+
}
310+
}
297311

298312
export const _createResourceReturn = <T>(opts?: ResourceOptions): ResourceReturnInternal<T> => {
299313
const resource: ResourceReturnInternal<T> = {

0 commit comments

Comments
 (0)