useResource()
function useResource(fetchShape: ReadShape, params: object | null):
  Denormalize<typeof fetchShape.schema>;
function useResource(...[fetchShape: ReadShape, params: object | null]):
  Denormalize<typeof fetchShape.schema>[];
function useResource<
  Params extends Readonly<object>,
  S extends Schema
>(fetchShape: ReadShape<S, Params>, params: Params | null): Denormalize<S>;
function useResource<
  Params extends Readonly<object>,
  S extends Schema
>(...[fetchShape: ReadShape<S, Params>, params: Params | null]): Denormalize<S>[];
Excellent for retrieving the data you need.
Cache policy is Stale-While-Revalidate by default but also configurable.
- Triggers fetch:
- On first-render
- or parameters change
 - or required entity is deleted
 - or imperative invalidation triggered
 
 - and When not in cache or result is considered stale
 - and When no identical requests are in flight
 - and when params are not null
 
 - On first-render
 - On Error (404, 500, etc):
- Throws error to be caught by Error Boundaries
 
 - While Loading:
- Returns previously cached if exists (even if stale)
 - Suspend rendering otherwise
 
 
Single
function Post({ id }: { id: number }) {
  const post = useResource(PostResource.detailShape(), { id });
  // post as PostResource
}
List
function Posts() {
  const posts = useResource(PostResource.listShape(), {});
  // posts as PostResource[]
}
Parallel
function Posts() {
  const [user, posts] = useResource(
    [UserResource.detailShape(), { id: userId }],
    [PostResource.listShape(), { userId }],
  );
  // user as UserResource
  // posts as PostResource[]
}
Sequential
function PostWithAuthor() {
  const post = useResource(PostResource.detailShape(), { id });
  // post as PostResource
  const author = useResource(UserResource.detailShape(), {
    id: post.userId,
  });
  // author as UserResource
}
Paginated data
When entities are stored in nested structures, that structure will remain.
export class PaginatedPostResource extends Resource {
  readonly id: number | null = null;
  readonly title: string = '';
  readonly content: string = '';
  static urlRoot = 'http://test.com/post/';
  static listShape<T extends typeof Resource>(this: T) {
    return {
      ...super.listShape(),
      schema: { results: [this.asSchema()], nextPage: '', lastPage: '' },
    };
  }
}
function ArticleList({ page }: { page: string }) {
  const { results: posts, nextPage, lastPage } = useResource(
    PaginatedPostResource.listShape(),
    { page },
  );
  // posts as PaginatedPostResource[]
}
Useful FetchShapes to send
Resource provides these built-in:
- detailShape()
 - listShape()
 
Feel free to add your own FetchShape as well.