useResource$()
This method allows you to generate computed values asynchronously. The asynchronous function passed as its first argument will be called when the component is mounted and when the tracked values change.
Just like all the use-
methods, it must be called within the context of component$()
, and all the hook rules apply.
const store = useStore({
bar: 'foo',
});
const resource = useResource$(async (ctx) => {
ctx.track(() => store.bar); // the resource will rerun when store.bar changes.
ctx.track(() => props.url); // track() can be called multiple times, to track multiple values
ctx.cleanup(() => {
// In case the resource need to be cleaned up, this function will be called.
// Allowing to clean resources like timers, subscriptions, fetch requests, etc.
});
// cleanup() can also be called multiple times.
ctx.cleanup(() => console.log('cleanup'));
// Resources can contain async computations
const value = await expensiveCompute(store.bar, props.url);
return value;
});
As we see in the example above, useResource$()
returns a ResourceReturn<T>
object that works like a glorified, fully reactive promise, containing the data and the resource state.
The state resource.state
can be one of the following strings:
'pending'
- the data is not yet available.'resolved'
- the data is available.'rejected'
- the data is not available due to an error or timeout.
The callback passed to useResource$()
runs right after the useTask$()
callbacks complete. Please refer to the Lifecycle section for more details.
<Resource />
<Resource />
is a component that renders its children when the resource is resolved, and renders a fallback when the resource is pending or rejected.
<Resource
value={weatherResource}
onPending={() => <div>Loading...</div>}
onRejected={() => <div>Failed to load weather</div>}
onResolved={(weather) => {
return <div>Temperature: {weather.temp}</div>;
}}
/>
It is worth noting that <Resource />
is not required when using useResource$()
. It is just a convenient way to render the resource state.
Examples :
-
useResource$()
with<Resource />
Example showing how useResource$
is used to perform a fetch call to the agify.io API. This will guess a person's age
based on the name typed by the user, and will update whenever the user types in the name input.
export default component$(() => {
const store = useStore<{ name?: string }>({
name: undefined,
});
const ageResource = useResource$<{
name: string;
age: number;
count: number;
}>(async ({ track, cleanup }) => {
track(() => store.name);
const abortController = new AbortController();
cleanup(() => abortController.abort('cleanup'));
const res = await fetch(`https://api.agify.io?name=${store.name}`, {
signal: abortController.signal,
});
return res.json();
});
return (
<div>
<h1>Enter your name, and I'll guess your age!</h1>
<input onInput$={(e: Event) => (store.name = (e.target as HTMLInputElement).value)} />
<Resource
value={ageResource}
onPending={() => <div>Loading...</div>}
onRejected={() => <div>Failed to person data</div>}
onResolved={(ageGuess) => {
return (
<div>
{store.name && (
<>
{ageGuess.name} {ageGuess.age} years
</>
)}
</div>
);
}}
/>
</div>
);
});
-
without<Resource />
The <Resource />
component is highly optimized for avoiding unnecessary re-renders and for SSR.
We strongly recommend that you use <Resource />
wherever possible. Occasionally, you may
encounter an edge case in which you need more control. For these situations, Qwik also lets you use
the data returned from useResource$()
directly in your TSX code, as shown below.
export default component$(() => {
const store = useStore<{ name?: string }>({
name: undefined,
});
const ageResource = useResource$<{
name: string;
age: number;
count: number;
}>(async ({ track, cleanup }) => {
track(() => store.name);
const abortController = new AbortController();
cleanup(() => abortController.abort('cleanup'));
const res = await fetch(`https://api.agify.io?name=${store.name}`, {
signal: abortController.signal,
});
return res.json();
});
return (
<div>
<h1>Enter your name, and I'll guess your age!</h1>
<input onInput$={(e: Event) => (store.name = (e.target as HTMLInputElement).value)} />
<div>
{ageResource.loading && <div>Loading age guess...</div>}
{!ageResource.loading && store.name && (
<div>
{ageResource.value.then((ageGuess) => (
<div>
{ageGuess.name} {ageGuess.age} years
</div>
))}
</div>
)}
</div>
</div>
);
});