服务器数据当作全局状态,8 个 useQuery 场景 + useMutation
@tanstack/react-query 是我们熟悉的 react-query 升级版,现在支持 vue 等,可以当做服务器数据的全局状态,主要写一下在 react 项目中 react-query 可简化很多逻辑处理,下面写一下基础用法,详情可查看文档。
npm i @tanstack/react-query
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// 可增加全局配置
refetchOnWindowFocus: false, // 窗口焦点重新获取
gcTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
});
const persister = createSyncStoragePersister({
storage: window.localStorage,
});
function App() {
return (
<PersistQueryClientProvider client={queryClient} persistOptions={{ persister }}>
<RouterProvider router={router} fallbackElement={<p>Loading...</p>} />
</PersistQueryClientProvider>
);
}
export default App;
下面自己安装组件库及路由即可,不再一一列举。可以封装 axios 请求,react-query 并不是一个 ajax 请求库,可以封装 fetch 或者 axios 用于请求,react-query 用于管理请求。
如:我们在一个列表页面,进来会请求加载接口,我们给这个请求命名为 applist,默认会触发一次 apiApplicationList() 是我们封装的请求接口,他的返回值会给 applicationList,applicationList 初始化为 undefined,请注意初始值设置。我们列表直接渲染 applicationList 即可,applicationList 是一个 state。
// 获取列表
const { data: applicationList, refetch: listRefetch } = useQuery({
queryKey: ["applist"],
queryFn: () => apiApplicationList(),
});
页面渲染有条件,如有 id 才查询,没有 id 不自动查询,enabled 可输入条件,控制是否初次触发。
const { data: applicationList, refetch: listRefetch } = useQuery({
queryKey: ["applist"],
queryFn: async () => {
const data = await apiApplicationList();
return data;
},
placeholderData: [], // 初始化的默认值,不设置则未 resolve 时返回 undefined
enabled: !!id,
});
apiApplicationList({name:1}) 可主动传参。
const { data: applicationList, refetch: listRefetch } = useQuery({
queryKey: ["applist"],
queryFn: () => apiApplicationList({ name: 1 }),
enabled: false,
});
接口返回数据不是我需要的,也可自定义 map 规则,applicationList 为我们处理后的数据,当然这种用法可以自定义自己请求前置的任何条件。
const { data: applicationList, refetch: listRefetch } = useQuery({
queryKey: ["applist"],
queryFn: async () => {
const data = await apiApplicationList({});
return data.map((item) => {
return {
...item,
id: item.application_id + 10,
};
});
},
enabled: false,
});
页面有个新增或者删除功能,操作成功后重新刷新接口,可主动触发 listRefetch(),重新请求拉取数据。
const reload = () => {
listRefetch();
};
如列表加载完之前不允许任何操作,可以拿到这个接口的请求状态 isPending,根据 isPending 可以自己的逻辑处理。
const { data: applicationList, refetch: listRefetch, isPending } = useQuery({
queryKey: ["applist"],
queryFn: () => apiApplicationList({ name: 1 }),
enabled: false,
});
每隔一段时间需要刷新一次接口,比如 10S 刷新一次列表,refetchInterval 可实现。
const { data: applicationList, refetch: listRefetch } = useQuery({
queryKey: ["applist"],
queryFn: () => apiApplicationList(),
refetchInterval: 10000,
});
查询条件变更则主动触发查询,避免 useEffect 频繁调用。我们可以借用 useImmer 来简化设置 state,当 listParams 变化后,重新自动请求接口。
// 初始化查询参数
const [listParams, setListParams] = useImmer({
kb_id: paramsQuery.kbid || "",
file_id: paramsQuery.fileid || "",
page_num: 1,
page_size: 9,
keyword: "",
});
// 页数切换
const onChangePage = (page: number, pageSize: number) => {
setListParams((draft) => {
draft.page_num = page;
draft.page_size = pageSize;
});
};
// 获取列表
const { data: chunklist, refetch: listRefetch } = useQuery({
queryKey: ["chunklist", listParams],
queryFn: () => apiChunkList(listParams),
});
useMutation 主要用于主动触发,如新增,删除,编辑等。
新增一个表单,点击确认保存,调用保存接口并刷新列表。apiApplicationAdd 为请求封装,addAppmutate 去了一个别名而已,后面可以调用这个 isPending 为请求状态。addAppmutate(values) 传递的参数,会传递给 apiApplicationAdd,onSuccess 回调成功刷新列表。
const { mutate: addAppmutate, isPending } = useMutation({
mutationFn: apiApplicationAdd,
onSuccess: () => {
reload();
},
});
// 新增应用
const handelAdd = async (values: any) => {
addAppmutate(values);
};