useSuspense()
Data Client's useSuspense() is more feature-rich and performant.
High performance async data rendering without overfetching.
useSuspense()
suspends rendering until the data is available. This is much like awaiting an async function. This avoids the complexity of handling loading and error conditions in your components by
centralizing them with a singular AsyncBoundary.
Usage
- Rest
- Promise
{"id":"1","fullName":"Einstein","bio":"Smart physicist"}
{"id":"2","fullName":"Elon Musk","bio":"CEO of Tesla, SpaceX and owner of Twitter"}
import { useSuspense } from '@rest-hooks/react';import { ProfileResource } from './api/Profile';function ProfileDetail(): JSX.Element {const profile = useSuspense(ProfileResource.get, { id: 1 });return (<div><h4>{profile.fullName}</h4><p>{profile.bio}</p></div>);}render(<ProfileDetail />);
import { useSuspense } from '@rest-hooks/react';import { getProfile } from './api/Profile';function ProfileDetail(): JSX.Element {const profile = useSuspense(getProfile, 1);return (<div><h4>{profile.fullName}</h4><p>{profile.bio}</p></div>);}render(<ProfileDetail />);
Behavior
Cache policy is Stale-While-Revalidate by default but also configurable.
Expiry Status | Fetch | Suspend | Error | Conditions |
---|---|---|---|---|
Invalid | yes1 | yes | no | not in store, deletion, invalidation, invalidIfStale |
Stale | yes1 | no | no | (first-render, arg change) & expiry < now |
Valid | no | no | maybe2 | fetch completion |
no | no | no | null used as second argument |
- Identical fetches are automatically deduplicated
- Hard errors to be caught by Error Boundaries
When using React Navigation, useSuspense() will trigger fetches on focus if the data is considered stale.
Use null
as the second argument on any rest hooks to indicate "do nothing."
// todo could be undefined if id is undefined
const todo = useSuspense(TodoResource.get, id ? { id } : null);
Types
- Type
- With Generics
function useSuspense(
endpoint: ReadEndpoint,
...args: Parameters<typeof endpoint> | [null]
): Denormalize<typeof endpoint.schema>;
function useSuspense<
E extends EndpointInterface<FetchFunction, Schema | undefined, undefined>,
Args extends readonly [...Parameters<E>] | readonly [null],
>(
endpoint: E,
...args: Args
): E['schema'] extends Exclude<Schema, null>
? Denormalize<E['schema']>
: ReturnType<E>;
Examples
List
[{"id":"1","fullName":"Einstein","bio":"Smart physicist"},{"id":"2","fullName":"Elon Musk","bio":"CEO of Tesla, SpaceX and owner of Twitter"}]
import { useSuspense } from '@rest-hooks/react';import { ProfileResource } from './api/Profile';function ProfileList(): JSX.Element {const profiles = useSuspense(ProfileResource.getList);return (<div>{profiles.map(profile => (<div key={profile.pk()}><h4>{profile.fullName}</h4><p>{profile.bio}</p></div>))}</div>);}render(<ProfileList />);
Sequential
When fetch parameters depend on data from another resource.
function PostWithAuthor() {
const post = useSuspense(PostResource.get, { id });
// post as Post
const author = useSuspense(UserResource.get, {
id: post.userId,
});
// author as User
}
Embedded data
When entities are stored in nested structures, that structure will remain.
export class PaginatedPost extends Entity {id = '';title = '';content = '';pk() {return this.id;}}export const getPosts = new RestEndpoint({path: '/post',searchParams: { page: '' },schema: { results: [PaginatedPost], nextPage: '', lastPage: '' },});
import { getPosts } from './api/Post';export default function ArticleList({ page }: { page: string }) {const {results: posts,nextPage,lastPage,} = useSuspense(getPosts, { page });return (<div>{posts.map(post => (<div key={post.pk()}>{post.title}</div>))}</div>);}
Todo App
Explore more Rest Hooks demos