Data mutations
Using our Create, Update, and Delete endpoints with Controller.fetch() reactively updates all appropriate components atomically (at the same time).
useController() gives components access to this global supercharged setState().
import { useController } from '@data-client/react'; import { TodoResource, type Todo } from './TodoResource'; export default function TodoItem({ todo }: { todo: Todo }) { const ctrl = useController(); const handleChange = e => ctrl.fetch( TodoResource.partialUpdate, { id: todo.id }, { completed: e.currentTarget.checked }, ); const handleDelete = () => ctrl.fetch(TodoResource.delete, { id: todo.id, }); return ( <div className="listItem nogap"> <label> <input type="checkbox" checked={todo.completed} onChange={handleChange} /> {todo.completed ? <strike>{todo.title}</strike> : todo.title} </label> <CancelButton onClick={handleDelete} /> </div> ); }
Rather than triggering invalidation cascades or using manually written update functions, Data Client reactively updates appropriate components using the fetch response.
Optimistic mutations based on previous state
import { resource } from '@data-client/rest'; import { Post } from './Post'; export { Post }; export const PostResource = resource({ path: '/posts/:id', searchParams: {} as { userId?: string | number } | undefined, schema: Post, }).extend('vote', { path: '/posts/:id/vote', method: 'POST', body: undefined, schema: Post, getOptimisticResponse(snapshot, { id }) { const post = snapshot.get(Post, { id }); if (!post) throw snapshot.abort; return { id, votes: post.votes + 1, }; }, });
getOptimisticResponse is just like setState with an updater function. Snapshot provides typesafe access to the previous store value, which we use to return the expected fetch response.
Reactive Data Client 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.
Tracking mutation loading
useLoading() enhances async functions by tracking their loading and error states.
import { useLoading, useController } from '@data-client/react'; import { PostResource } from './PostResource'; import PostForm from './PostForm'; export default function PostCreate({ navigateToPost }) { const ctrl = useController(); const [handleSubmit, loading, error] = useLoading( async data => { const post = await ctrl.fetch(PostResource.getList.push, data); navigateToPost(post.id); }, [ctrl], ); return ( <PostForm onSubmit={handleSubmit} loading={loading} error={error} /> ); }