Resources
useResource is createResource with one change: it returns a single object instead of a [resource, actions] tuple.
useResource is createResource with one change: it returns a single object instead of a [resource, actions]
tuple. Everything the tuple held is a property of that object.
| Solid | ShadowJS | What differs |
|---|---|---|
const [user, { refetch, mutate }] = createResource(id, fetchUser) |
const user = useResource(id, fetchUser); user.refetch(), user.mutate(v) |
Not iterable: destructuring as a tuple fails. refetch() returns a promise for the new value (undefined when the fetch failed or a newer one replaced it; it never rejects) and takes no info argument. mutate(v) returns the new value. |
createResource(fetcher) |
useResource(fetcher) |
Same: fetches once. |
createResource(source, fetcher) |
useResource(source, fetcher) |
source is an accessor, or a plain value that fetches once. false, null and undefined skip the fetch and leave the state "unresolved". |
fetcher(source, { value, refetching }) |
fetcher(source, { value, refetching, signal }) |
refetching is a boolean. signal is an AbortSignal aborted when a newer fetch starts or the owner is disposed. |
user(), user.state, user.loading, user.error, user.latest |
same | state is "unresolved" | "pending" | "ready" | "refreshing" | "errored". Reading user() after a failure throws to the nearest ErrorBoundary. |
{ initialValue } |
{ initialValue } |
The value is readable at once and the fetch still runs, so state is "refreshing" until it settles. |
{ name, deferStream } |
{ name, deferStream, key } |
key is the server serialization key. deferStream holds the streamed first flush until the resource settles, as in Solid (see holding the first flush). ssrLoadFrom, storage and onHydrated are not implemented. |
<Suspense> holds while a resource is read |
same | The boundary around where the resource is read shows its fallback while the first load is pending. A refetch never re-suspends. |
Solid (not executed):
tsx
import { createResource, createSignal, ErrorBoundary, Show, Suspense } from "solid-js";
function Profile(props) {
const [id, setId] = createSignal(1);
const [user, { refetch }] = createResource(id, props.load);
return (
<ErrorBoundary fallback={(err) => <p role="alert">Failed: {String(err)}</p>}>
<Suspense fallback={<p>Loading</p>}>
<h2>{user()?.name}</h2>
</Suspense>
<button onClick={() => setId((n) => n + 1)}>Next</button>
<Show when={user.state === "refreshing"}>
<small>updating</small>
</Show>
</ErrorBoundary>
);
}
ShadowJS:
src/profile.tsxtsx
import { useResource, useStore } from "@shadow-js/core";
import { ErrorBoundary, Show, Suspense } from "@shadow-js/web";
export interface User {
id: number;
name: string;
}
export function Profile(props: { load: (id: number) => Promise<User> }) {
const [id, setId] = useStore(1);
const user = useResource(id, (userId) => props.load(userId));
return (
<ErrorBoundary fallback={(error) => <p role="alert">Failed: {String(error)}</p>}>
<Suspense fallback={<p>Loading</p>}>
<h2>{user()?.name}</h2>
</Suspense>
<button type="button" onClick={() => setId((n) => n + 1)}>
Next
</button>
<Show when={user.state === "refreshing"}>
<small>updating</small>
</Show>
</ErrorBoundary>
);
}
/** A loader that never settles, so the mount check sees the fallback. */
export function LoadingProfile() {
return <Profile load={() => new Promise<User>(() => undefined)} />;
}