Skip to main content
Version: 2.2

Usage without Suspense

Some libraries you work with may take a loading or error state. In these cases you might want a boolean loading instead of yielding to Suspense.

In any case, here's a sample hook you can adapt to use that information in any way you please.

Sample Hook

useStatefulResource.tsx

import { useRetrieve, useCache, useError, Schema, ReadShape } from 'rest-hooks';

/** If the invalidIfStale option is set we suspend if resource has expired */
function hasUsableData<
S extends Schema,
Params extends Readonly<object>,
Body extends Readonly<object | string> | void
>(
resource: RequestResource<ReadShape<S, Params, Body>> | null,
fetchShape: ReadShape<S, Params, Body>,
) {
return !(
(fetchShape.options && fetchShape.options.invalidIfStale) ||
!resource
);
}

/** Ensure a resource is available; loading and error returned explicitly. */
function useStatefulResource<
Params extends Readonly<object>,
Body extends Readonly<object | string> | void,
S extends Schema
>(fetchShape: ReadShape<S, Params, Body>, params: Params | null) {
let maybePromise = useRetrieve(fetchShape, params);
const resource = useCache(fetchShape, params);

const loading =
!hasUsableData(resource, fetchShape) &&
maybePromise &&
typeof maybePromise.then === 'function';

let error = useError(fetchShape, params, resource);

return {
data: resource as NonNullable<typeof resource>,
loading,
error,
};
}

Hook usage

resources/ProfileResource.ts

export default class ProfileResource extends Resource {
readonly id: number | null = null;
readonly img: string = '';
readonly fullName: string = '';
readonly bio: string = '';

pk() {
return this.id;
}
static urlRoot = '/profiles';
}

ProfileList.tsx

import { Skeleton, Card, Avatar } from 'antd';
import ProfileResource from 'resources/ProfileResource';

import useStatefulResource from './useStatefulResource';

const { Meta } = Card;

function ProfileList() {
const { data, loading, error } = useStatefulResource(
ProfileResource.detailShape(),
{},
);
if (error) return <div>Error {error.status}</div>
return (
<Card style={{ width: 300, marginTop: 16 }} loading={loading}>
<Meta
avatar={
<Avatar src={data.img} />
}
title={data.fullName}
description={data.bio}
/>
</Card>
);
}