useController()
Controller provides type-safe methods to access and dispatch actions to the store.
For instance fetch, invalidate, and setResponse
import { useController } from '@data-client/react';
function MyComponent({ id }) {
const ctrl = useController();
const handleRefresh = useCallback(
async e => {
await ctrl.fetch(MyResource.get, { id });
},
[fetch, id],
);
const handleSuspend = useCallback(
async e => {
await ctrl.invalidate(MyResource.get, { id });
},
[invalidate, id],
);
const handleLogout = useCallback(
async e => {
ctrl.resetEntireStore();
},
[resetEntireStore],
);
}
Examples
Form submission
fetch returns the denormalized response, matching useSuspense()'s return type. This allows using Entity methods like pk().
function CreatePost() {
const ctrl = useController();
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
const post = await ctrl.fetch(
PostResource.getList.push,
new FormData(e.target as HTMLFormElement),
);
post.title;
post.computedField;
navigate(`/post/${post.pk()}`);
};
return <form onSubmit={handleSubmit}>{/* fields */}</form>;
}
Direct entity update
Use set for immediate updates without network requests. Supports functional updates to avoid race conditions.
function VoteButton({ articleId }: { articleId: string }) {
const ctrl = useController();
return (
<button
onClick={() =>
ctrl.set(Article, { id: articleId }, article => ({
...article,
votes: article.votes + 1,
}))
}
>
Vote
</button>
);
}
Invalidate after mutation
Force refetch of related data using invalidate or expireAll.
function ClearUserCache({ userId }: { userId: string }) {
const ctrl = useController();
const handleClear = async () => {
// invalidate() causes suspense; expireAll() refetches silently
ctrl.expireAll(UserResource.get);
ctrl.expireAll(UserResource.getList);
};
return <button onClick={handleClear}>Refresh user data</button>;
}
For better performance and consistency, prefer including side effect updates in mutation responses.
Prefetching
Use fetchIfStale to prefetch without overfetching fresh data.
function ArticleLink({ id }: { id: string }) {
const ctrl = useController();
return (
<Link
to={`/article/${id}`}
onMouseEnter={() => ctrl.fetchIfStale(ArticleResource.get, { id })}
>
Read more
</Link>
);
}
Websocket updates
Populate cache with external data via set.
function useWebsocket(url: string) {
const ctrl = useController();
useEffect(() => {
const ws = new WebSocket(url);
ws.onmessage = event => {
const { entity, args, data } = JSON.parse(event.data);
ctrl.set(EntityMap[entity], args, data);
};
return () => ws.close();
}, [ctrl, url]);
}
For production use, implement a Manager for data streams rather than component-level effects. Managers handle connection lifecycle globally and work with SSR.