跳到主要内容

useController()

Controller 提供类型安全的方法,用于访问 store 并向其分发 action。

例如 fetch、invalidate 和 setResponse

import { useCallback } from 'react';
import { useController } from '@data-client/react';
import { MyResource } from './resources';

function MyComponent({ id }: { id: string }) {
const ctrl = useController();

const handleRefresh = useCallback(
async e => {
await ctrl.fetch(MyResource.get, { id });
},
[ctrl, id],
);

const handleSuspend = useCallback(
async e => {
await ctrl.invalidate(MyResource.get, { id });
},
[ctrl, id],
);

const handleLogout = useCallback(
async e => {
ctrl.resetEntireStore();
},
[ctrl],
);
}

示例​

表单提交​

fetch 返回反规范化后的响应,与 useSuspense() 的返回类型一致。这样就可以使用 pk() 等 Entity 方法。

import type { FormEvent } from 'react';
import { useNavigate } from 'react-router';
import { useController } from '@data-client/react';
import { PostResource } from './PostResource';

function CreatePost() {
const ctrl = useController();
const navigate = useNavigate();

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>;
}

直接更新 Entity​

使用 set 可以在不发起网络请求的情况下立即更新。它支持函数式更新,以避免竞态条件。

import { useController } from '@data-client/react';
import { Article } from './Article';

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 或 expireAll 强制重新获取相关数据。

import { useController } from '@data-client/react';
import { UserResource } from './UserResource';

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>;
}
提示

为了获得更好的性能和一致性,优先在变更响应中包含副作用更新。

预取​

使用 fetchIfStale 进行预取,而不会重复获取仍然新鲜的数据。

import { Link } from 'react-router';
import { useController } from '@data-client/react';
import { ArticleResource } from './ArticleResource';

function ArticleLink({ id }: { id: string }) {
const ctrl = useController();

return (
<Link
to={`/article/${id}`}
onMouseEnter={() => ctrl.fetchIfStale(ArticleResource.get, { id })}
>
Read more
</Link>
);
}

Websocket 更新​

通过 set 用外部数据填充缓存。

import { useEffect } from 'react';
import { useController } from '@data-client/react';
import { EntityMap } from './resources';

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]);
}
注意

在生产环境中,请实现一个用于数据流的 Manager,而不是使用组件级的 effect。Manager 会在全局处理连接的生命周期,并且支持 SSR。

Todo 应用​

更多演示