useFetch()
Fetch an Endpoint if it is not in cache or stale. Returns a thenable that works with
React.use() -- use(useFetch(endpoint, args)) operates
like useSuspense(), suspending when data is loading, returning denormalized data when
available, and re-suspending on invalidation.
Usage
Parallel data loading
Since useFetch() and use() are separate calls, multiple fetches start in parallel — even when the first use() suspends. See the parallel fetches example below.
import { use } from 'react'; import { useFetch } from '@data-client/react'; import { PostResource, CommentResource } from './Resources'; function PostWithComments({ id }: { id: number }) { // Both fetches start in parallel const postPromise = useFetch(PostResource.get, { id }); const commentsPromise = useFetch(CommentResource.getList, { postId: id, }); // use() reads the results — if the first suspends, // the second fetch is already in-flight const post = use(postPromise); const comments = use(commentsPromise); return ( <article> <h3>{post.title}</h3> <p>{post.body}</p> <h4>Comments</h4> {comments.map(comment => ( <div key={comment.id} className="listItem"> <strong>{comment.author}</strong>: {comment.text} </div> ))} </article> ); } render(<PostWithComments id={1} />);
Prefetching
useFetch() can also be used standalone to ensure resources are available early in a render tree before they are needed.
Use in combination with a data-binding hook (useCache(), useSuspense(), useDLE(), useLive()) in another component.
function MasterPost({ id }: { id: number }) {
useFetch(PostResource.get, { id });
// ...
}
Behavior
| Expiry Status | Fetch | use() behavior | resolved | Conditions |
|---|---|---|---|---|
| Invalid | yes1 | suspends | false | not in store, deletion, invalidation |
| Stale | yes1 | suspends | false | (first-render, arg change) & expiry < now |
| Valid | no | returns data | true | fetch completion |
| Error | no | throws error | true | fetch failed, caught by Error Boundary |
| no | undefined | null used as second argument |
When the store updates (e.g., via mutations or Controller.set()), the component
re-renders and useFetch() returns updated denormalized data automatically.
- Identical fetches are automatically deduplicated
When using React Navigation, useFetch() will trigger fetches on focus if the data is considered stale.
:::
Use null as the second argument to any Data Client hook means "do nothing."
// todo could be undefined if id is undefined
const todo = useFetch(TodoResource.get, id ? { id } : null);
Types
- Type
- With Generics
function useFetch(
endpoint: ReadEndpoint,
...args: Parameters<typeof endpoint> | [null]
): (PromiseLike<any> & { resolved: boolean }) | undefined;
function useFetch<
E extends EndpointInterface<
FetchFunction,
Schema | undefined,
undefined
>,
Args extends readonly [...Parameters<E>] | readonly [null],
>(endpoint: E, ...args: Args): UsablePromise<Denormalize<E['schema']>>;
Examples
Checking fetch status
Use promise.resolved to check whether data is still loading:
function MasterPost({ id }: { id: number }) {
const promise = useFetch(PostResource.get, { id });
if (!promise.resolved) {
// fetch is in-flight
}
// ...
}
NextJS Preload
To prevent fetch waterfalls in NextJS, sometimes you might need to add preloads to top level routes.
Explore the example