Data mutations
Tell react to update
Just like setState(), we must make React aware of the any mutations so it can rerender.
Controller from useController provides this functionality in a type-safe manner. Controller.fetch() lets us trigger async mutations.
import { useSuspense } from '@rest-hooks/react';import { TodoResource } from './api/Todo';function TodoDetail({ id }: { id: number }) {const todo = useSuspense(TodoResource.get, { id });const ctrl = useController();const updateWith = title => () =>ctrl.fetch(TodoResource.partialUpdate, { id }, { title });return (<div><div>{todo.title}</div><button onClick={updateWith('🥑')}>🥑</button><button onClick={updateWith('💖')}>💖</button></div>);}render(<TodoDetail id={1} />);
Rest Hooks uses the fetch response to safely update all components. This not only more than doubles performance, but dramatically reduces server load that comes up sequential fetches.
Tracking imperative loading/error state
useLoading() enhances async functions by tracking their loading and error states.
import { useController } from '@rest-hooks/react';
import { useLoading } from '@rest-hooks/hooks';
function ArticleEdit() {
const ctrl = useController();
const [handleSubmit, loading, error] = useLoading(
data => ctrl.fetch(todoUpdate, { id }, data),
[ctrl],
);
return <ArticleForm onSubmit={handleSubmit} loading={loading} />;
}
React 18 version with useTransition
import { useTransition } from 'react';
import { useController } from '@rest-hooks/react';
import { useLoading } from '@rest-hooks/hooks';
function ArticleEdit() {
const ctrl = useController();
const [loading, startTransition] = useTransition();
const handleSubmit = data =>
startTransition(() => ctrl.fetch(todoUpdate, { id }, data));
return <ArticleForm onSubmit={handleSubmit} loading={loading} />;
}
Zero delay mutations
Controller.fetch call the mutation endpoint, and update React based on the response. While useTransition improves the experience, the UI still ultimately waits on the fetch completion to update.
For many cases like toggling todo.completed, incrementing an upvote, or dragging and drop a frame this can be too slow!
import { useController } from '@rest-hooks/react';import { TodoResource, Todo } from './api/Todo';export function TodoItem({ todo }: { todo: Todo }) {const ctrl = useController();return (<label style={{ display: 'block' }}><inputtype="checkbox"checked={todo.completed}onChange={e =>ctrl.fetch(TodoResource.partialUpdate,{ id: todo.id },{ completed: e.currentTarget.checked },)}/>{todo.completed ? <strike>{todo.title}</strike> : todo.title}</label>);}
getOptimisticResponse is just like setState with an updater function. Using snap for access to the store to get the previous value, as well as the fetch arguments, we return the expected fetch response.
export const updateTodo = new RestEndpoint({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos/:id',
method: 'PUT',
schema: Todo,
getOptimisticResponse(snap, { id }, body) {
return {
id,
...body,
};
},
});
Rest Hooks ensures data integrity against any possible networking failure or race condition, so don't worry about network failures, multiple mutation calls editing the same data, or other common problems in asynchronous programming.