@tanstack/react-query@5.35.5
1. isPending isLoading isFetching 傻傻分不清
const { data: knowledgeList, isFetching: loading } = useQuery({
queryKey: ['knowledgeList'],
initialData: [],
gcTime: 0,
});
useQuery的isFetching是接口在请求中
React Query 5: When to use isLoading, isFetching, and isRefetching
2. 单独获取一个接口的loading状态
useQuery()会返回isFetching,但是往往component是分开写的,就是发起请求在一个component,而Spin在另一个component,这时候就需要独立的拿到isFetching
import {
useIsFetching,
} from '@tanstack/react-query';
export const useKnowledgeDetailIsFetching = () => {
return useIsFetching({ queryKey: ['knowledgeDetail'] }) > 0;
};
注意:useIsFetching()返回的是数字
Background Fetching Indicators
同理,对于useMutation
export const useChunkIsTesting = () => {
return useIsMutating({ mutationKey: ['testChunk'] }) > 0;
};
3. queryClient.getQueryData与useQuery获取共享数据的区别
比如页面加载的时候使用useQuery
请求到了数据,被@tanstack/react-query缓存了起来,在其他组件里想拿到该数据,通常会直接调用useQuery获取数据,但是在项目里出了问题,如下图,我在两个节点拖拽无法建立连线,因为线跟后端返回的数据是管理的,边节点里面调用了useQuery
,每次有新线连接就会调用useQuery
,这样导致我客户端的数据被接口返回的数据所覆盖,从而连接不成功。根本原因在于retryOnMount
参数为true
,在每次挂载组件时自动触发重新获取。
换queryClient.getQueryData
就不会在拖线的时候发送请求了
const queryClient = useQueryClient();
const flowDetail = queryClient.getQueryData<IFlow>(['flowDetail']);
4. 在不同组件共享useMutation获取的数据
通常都是用useQuery
去获取代get方法的接口的数据的,但是有时候后端给的接口是post,需要提交表单数据,这个时候需要用button触发接口的调用,如果用useQuery
的话,需要使用enabled:false
禁用useQuer
y的默认加载调用的行为,然后结合refetch
函数去手动调用,但是refetch
不能传递参数,需要将参数传到state或者redux、zustand等状态管理库托管,所以还是用useMutation
方便点,但是怎么在不同组件共享useMutation
获取的数据?
export const useSelectTestingResult = (): ITestingResult => {
const data = useMutationState({
filters: { mutationKey: ['testChunk'] },
select: (mutation) => {
return mutation.state.data;
},
});
return (data.at(-1) ?? { // 获取接口返回的最新的一条数据
chunks: [],
documents: [],
total: 0,
}) as ITestingResult;
};
5. 模糊匹配useQuery缓存的数据
列表页面往往有很多查询条件,比如分页,搜索,排序等,@tanstack/react-query@5.35.5推荐将查询条件写进queryKey作为依赖,从而触发接口的重新请求,但是我们在不同的组件希望拿到被@tanstack/react-query@5.35.5缓存的数组,而不是层层传递,useQuery代码如下,
如果在不同的组件里使用useFetchNextChunkList,如果有组件mount,则useFetchNextChunkList会被多次执行,会导致每次的查询条件都是初始值,因为useState会被重新执行,所以只好选择 getQueriesData
, Share state between components #2310 这种方式行不通
export const useSelectChunkList = () => {
const queryClient = useQueryClient();
const data = queryClient.getQueriesData<{
data: IChunk[];
total: number;
documentInfo: IKnowledgeFile;
}>({ queryKey: ['fetchChunkList'] });
return data[0][1];
};
6. 缓存全局数据
Using react-query to store global state? #2852
7. 在发出请求到拿到数据中间会返回初始值initialData
我想保留之前的查询结果,会导致组件无效的rerender,即使用了placeholderData: keepPreviousData
也不行。有待讨论
8. 自定义staleTime: 20 * 1000
,设置initialData跟不设置该值有不同的表现
阅读了 React Query as a State Manager 自己做了如下测试
demo.tsx
import { useFetchFlowTemplates } from '@/hooks/flow-hooks';
const Inner = () => {
const ret = useFetchFlowTemplates();
const data = ret?.data;
return <ul>{data?.map((x) => <li key={x.id}>{x.title}</li>)}</ul>;
};
const Demo = () => {
const ret = useFetchFlowTemplates();
const data = ret?.data;
return (
<section>
<h6>{data?.length}</h6>
{data && <Inner></Inner>}
</section>
);
};
export default Demo;
hooks
export const useFetchFlowTemplates = (): ResponseType<IFlowTemplate[]> => {
const { data } = useQuery({
queryKey: ['fetchFlowTemplates'],
staleTime: 20 * 1000,
initialData: [],
queryFn: async () => {
const { data } = await flowService.listTemplates();
return data;
},
});
return data;
};
上述代码不会发送请求,将initialData注释掉,如下,可以正常发送一条请求,有待进一步探究
export const useFetchFlowTemplates = (): ResponseType<IFlowTemplate[]> => {
const { data } = useQuery({
queryKey: ['fetchFlowTemplates'],
staleTime: 20 * 1000,
// initialData: [],
queryFn: async () => {
const { data } = await flowService.listTemplates();
return data;
},
});
return data;
};
9. useQuery分页查询,会重置之前请求到的数据
上代码:
export const useFetchNextChunkList = (): ResponseGetType<{
data: IChunk[];
total: number;
documentInfo: IKnowledgeFile;
}> &
IChunkListResult => {
const { pagination, setPagination } = useGetPaginationWithRouter();
const { documentId } = useGetKnowledgeSearchParams();
const { searchString, handleInputChange } = useHandleSearchChange();
const [available, setAvailable] = useState<number | undefined>();
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
const { data, isFetching: loading } = useQuery({
queryKey: [
'fetchChunkList',
documentId,
pagination.current,
pagination.pageSize,
debouncedSearchString,
available,
],
initialData: { data: [], total: 0, documentInfo: {} },
// placeholderData: keepPreviousData,
gcTime: 0,
queryFn: async () => {
const { data } = await kbService.chunk_list({
doc_id: documentId,
page: pagination.current,
size: pagination.pageSize,
available_int: available,
keywords: searchString,
});
if (data.code === 0) {
const res = data.data;
return {
data: res.chunks,
total: res.total,
documentInfo: res.doc,
};
}
return (
data?.data ?? {
data: [],
total: 0,
documentInfo: {},
}
);
},
});
const onInputChange: React.ChangeEventHandler<HTMLInputElement> = useCallback(
(e) => {
setPagination({ page: 1 });
handleInputChange(e);
},
[handleInputChange, setPagination],
);
const handleSetAvailable = useCallback(
(a: number | undefined) => {
setPagination({ page: 1 });
setAvailable(a);
},
[setAvailable, setPagination],
);
return {
data,
loading,
pagination,
setPagination,
searchString,
handleInputChange: onInputChange,
available,
handleSetAvailable,
};
};
每次在切换页码的时候useQuery之前返回的数据都会被重置为initialData,从而导致依赖该数据的页面其他部分在切换页面的时候被重新渲染。但是按照官网的示例做加上参数 placeholderData: keepPreviousData,
并不能完全解决这个问题Paginated / Lagged Queries
解决办法:
删除initialData
,加上
placeholderData: (previousData) => previousData ?? []
Conflict Between initialData and placeholderData – Either undefined or UI Flicker #8183
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。