REST
- NPM
- Yarn
yarn add @rest-hooks/rest@5.1
npm install --save @rest-hooks/rest@5.1
Define a Resource
- TypeScript
- JavaScript
import { Resource } from '@rest-hooks/rest';
export default class ArticleResource extends Resource {
readonly id: number | undefined = undefined;
readonly title: string = '';
readonly content: string = '';
readonly author: number | null = null;
readonly tags: string[] = [];
pk() {
return this.id?.toString();
}
static urlRoot = 'http://test.com/article/';
}
import { Resource } from '@rest-hooks/rest';
export default class ArticleResource extends Resource {
id = undefined;
title = '';
content = '';
author = null;
tags = [];
pk() {
return this.id?.toString();
}
static urlRoot = 'http://test.com/article/';
}
Resources are immutable. Use readonly
in typescript to enforce this.
Default values ensure simpler types, which means less conditionals in your components.
pk() is essential to tell Rest Hooks how to normalize the data. This ensures consistency and the best performance characteristics possible.
static urlRoot is used as the basis of common url patterns
APIs quickly get much more complicated! Customizing Resources to fit your API
Use the Resource
- Single
- List
import { useSuspense } from 'rest-hooks';
import ArticleResource from 'resources/article';
export default function ArticleDetail({ id }: { id: number }) {
const article = useSuspense(ArticleResource.detail(), { id });
return (
<article>
<h2>{article.title}</h2>
<div>{article.content}</div>
</article>
);
}
import { useSuspense } from 'rest-hooks';
import ArticleResource from 'resources/article';
import ArticleSummary from './ArticleSummary';
export default function ArticleList({ sortBy }: { sortBy: string }) {
const articles = useSuspense(ArticleResource.list(), { sortBy });
return (
<section>
{articles.map(article => (
<ArticleSummary key={article.pk()} article={article} />
))}
</section>
);
}
useSuspense() guarantees access to data with sufficient freshness. This means it may issue network calls, and it may suspend until the fetch completes. Param changes will result in accessing the appropriate data, which also sometimes results in new network calls and/or suspends.
- Fetches are centrally controlled, and thus automatically deduplicated
- Data is centralized and normalized guaranteeing consistency across uses, even with different endpoints.
- (For example: navigating to a detail page with a single entry from a list view will instantly show the same data as the list without requiring a refetch.)
Dispatch mutation
- Create
- Update
- Delete
import { useController } from 'rest-hooks';
import ArticleResource from 'resources/article';
export default function NewArticleForm() {
const { fetch } = useController();
return (
<Form onSubmit={e => fetch(ArticleResource.create(), new FormData(e.target))}>
<FormField name="title" />
<FormField name="content" type="textarea" />
<FormField name="tags" type="tag" />
</Form>
);
}
create() then takes any keyable
body to send as the payload and then returns a promise that
resolves to the new Resource created by the API. It will automatically be added in the cache for any consumers to display.
import { useController } from 'rest-hooks';
import ArticleResource from 'resources/article';
export default function UpdateArticleForm({ id }: { id: number }) {
const article = useSuspense(ArticleResource.detail(), { id });
const { fetch } = useController();
return (
<Form
onSubmit={e => fetch(ArticleResource.update(), { id }, new FormData(e.target))}
initialValues={article}
>
<FormField name="title" />
<FormField name="content" type="textarea" />
<FormField name="tags" type="tag" />
</Form>
);
}
update() then takes any keyable
body to send as the payload and then returns a promise that
then takes any keyable
body to send as the payload and then returns a promise that
resolves to the new Resource created by the API. It will automatically be added in the cache for any consumers to display.
import { useController } from 'rest-hooks';
import ArticleResource from 'resources/article';
export default function ArticleWithDelete({ article }: { article: ArticleResource }) {
const { fetch } = useController();
return (
<article>
<h2>{article.title}</h2>
<div>{article.content}</div>
<button onClick={() => fetch(ArticleResource.delete(), { id: article.id })}>Delete</button>
</article>
);
}
We use FormData in the example since it doesn't require any opinionated form state management solution. Feel free to use whichever one you prefer.
Mutations automatically update the normalized cache, resulting in consistent and fresh data.