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.