React Query:使用具有不同设置的多个查询客户端?
ReactQuery: use multiple QueryClients with different settings?
我有一个 React SPA,我正在尝试使用 ReactQuery
库。我从我的服务器 API 获取了很多不同的数据——有些比较不稳定,有些则相对稳定。我想将稳定的数据存储在浏览器的本地存储中,以减少对服务器的请求数量(从而使 UI 应用程序更快一些)并每 24 小时刷新一次。但是,我想在每次请求时使易失性数据无效并重新获取,因为其他用户可能同时更改了数据。
目前,我的应用程序只有一个简单的 ReactQueryProvider:
import React from 'react';
import { QueryClientProvider } from 'react-query';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import { CookiesProvider } from 'react-cookie';
import { QueryClient } from 'react-query';
import { HttpError } from 'types';
const MaxFailureCount = 2;
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href') || undefined;
const rootElement = document.getElementById('root');
function shouldRetry(failureCount: number, error: unknown): boolean {
if (error instanceof HttpError) {
if (!error.isRetryableError()) {
return false;
}
}
return failureCount < MaxFailureCount;
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: shouldRetry,
},
},
});
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<BrowserRouter basename={baseUrl}>
<CookiesProvider>
<App />
</CookiesProvider>
</BrowserRouter>
</QueryClientProvider>,
rootElement
);
如果我想使用 persistQueryClient 插件,我必须将 cacheTime
添加到我的 queryClient
,然后将客户端传递给 localStoragePersistor
。如果我正确理解了文档,这将改变我所有的 useQuery
调用以将数据存储在本地存储中 24 小时 - 即使是更不稳定的数据。
可以选择将 buster
字符串传递给持久化缓存,但这会使所有数据(甚至稳定数据)失效。有没有办法使用两个不同的queryClients
?或者,在每个 useQuery
调用中指定是否应缓存响应?或者我应该手动将稳定数据 API 的响应放入本地存储?如果是这样,我该如何处理失效?
QueryClient 只是一个包含 queryCache、mutationCache 和默认设置的容器。如果您想创建一个新的 QueryClient,并复制缓存,您可以设置新的默认设置并仍然保留缓存。
一个基本的实现是:
const ReactQueryConfigProvider = ({ children, defaultOptions }) => {
const client = useQueryClient()
const [newClient] = React.useState(
() =>
new QueryClient({
queryCache: client.getQueryCache(),
muationCache: client.getMutationCache(),
defaultOptions,
})
)
return <QueryClientProvider client={newClient}>{children}</QueryClientProvider>
}
这是一个codesandbox where this is used in action (I've taken this from my blog: https://tkdodo.eu/blog/testing-react-query)。不确定这是否会帮助您解决 persistQueryClient 问题,但如果您只想更改 componentTree 某些部分的 cacheTime
,这就可以了。
最终,我决定创建自己的本地存储挂钩。这很简单,我可以重新使用默认的 QueryClient
而无需任何额外的配置:
import { HttpError } from 'types';
import { useQuery, UseQueryOptions } from 'react-query';
import { get } from 'utils/request';
const defaultStaleTime = 24 * 60 * 60 * 1000; // 24 hours
type CachedQueryProps<T> = {
path: string;
staleTime?: number | null | undefined;
forceRefresh?: boolean | null | undefined;
options?: UseQueryOptions<T, HttpError, T> | undefined;
};
type CachedQueryResult<T> = {
data: T | undefined;
isLoading: boolean;
error: HttpError | null;
};
type CachedObject<T> = {
data: T;
refreshTime: number;
};
export function useCachedQuery<T>(props: CachedQueryProps<T>): CachedQueryResult<T> {
const now = new Date().getTime();
const lowestAllowedTime = now - (props.staleTime ?? defaultStaleTime);
const item = window.localStorage.getItem(props.path);
const result = (item && (JSON.parse(item) as CachedObject<T> | null)) || null;
const refresh = !result || result.refreshTime < lowestAllowedTime || !!props.forceRefresh;
const query = useQuery<T, HttpError>(props.path, () => get<T>(props.path), { ...props.options, enabled: refresh });
if (query.isSuccess) {
const newData = { data: query.data, refreshTime: now };
localStorage.setItem(props.path, JSON.stringify(newData));
return query;
}
return { data: result?.data, isLoading: false, error: null };
}
我有一个 React SPA,我正在尝试使用 ReactQuery
库。我从我的服务器 API 获取了很多不同的数据——有些比较不稳定,有些则相对稳定。我想将稳定的数据存储在浏览器的本地存储中,以减少对服务器的请求数量(从而使 UI 应用程序更快一些)并每 24 小时刷新一次。但是,我想在每次请求时使易失性数据无效并重新获取,因为其他用户可能同时更改了数据。
目前,我的应用程序只有一个简单的 ReactQueryProvider:
import React from 'react';
import { QueryClientProvider } from 'react-query';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import { CookiesProvider } from 'react-cookie';
import { QueryClient } from 'react-query';
import { HttpError } from 'types';
const MaxFailureCount = 2;
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href') || undefined;
const rootElement = document.getElementById('root');
function shouldRetry(failureCount: number, error: unknown): boolean {
if (error instanceof HttpError) {
if (!error.isRetryableError()) {
return false;
}
}
return failureCount < MaxFailureCount;
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: shouldRetry,
},
},
});
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<BrowserRouter basename={baseUrl}>
<CookiesProvider>
<App />
</CookiesProvider>
</BrowserRouter>
</QueryClientProvider>,
rootElement
);
如果我想使用 persistQueryClient 插件,我必须将 cacheTime
添加到我的 queryClient
,然后将客户端传递给 localStoragePersistor
。如果我正确理解了文档,这将改变我所有的 useQuery
调用以将数据存储在本地存储中 24 小时 - 即使是更不稳定的数据。
可以选择将 buster
字符串传递给持久化缓存,但这会使所有数据(甚至稳定数据)失效。有没有办法使用两个不同的queryClients
?或者,在每个 useQuery
调用中指定是否应缓存响应?或者我应该手动将稳定数据 API 的响应放入本地存储?如果是这样,我该如何处理失效?
QueryClient 只是一个包含 queryCache、mutationCache 和默认设置的容器。如果您想创建一个新的 QueryClient,并复制缓存,您可以设置新的默认设置并仍然保留缓存。
一个基本的实现是:
const ReactQueryConfigProvider = ({ children, defaultOptions }) => {
const client = useQueryClient()
const [newClient] = React.useState(
() =>
new QueryClient({
queryCache: client.getQueryCache(),
muationCache: client.getMutationCache(),
defaultOptions,
})
)
return <QueryClientProvider client={newClient}>{children}</QueryClientProvider>
}
这是一个codesandbox where this is used in action (I've taken this from my blog: https://tkdodo.eu/blog/testing-react-query)。不确定这是否会帮助您解决 persistQueryClient 问题,但如果您只想更改 componentTree 某些部分的 cacheTime
,这就可以了。
最终,我决定创建自己的本地存储挂钩。这很简单,我可以重新使用默认的 QueryClient
而无需任何额外的配置:
import { HttpError } from 'types';
import { useQuery, UseQueryOptions } from 'react-query';
import { get } from 'utils/request';
const defaultStaleTime = 24 * 60 * 60 * 1000; // 24 hours
type CachedQueryProps<T> = {
path: string;
staleTime?: number | null | undefined;
forceRefresh?: boolean | null | undefined;
options?: UseQueryOptions<T, HttpError, T> | undefined;
};
type CachedQueryResult<T> = {
data: T | undefined;
isLoading: boolean;
error: HttpError | null;
};
type CachedObject<T> = {
data: T;
refreshTime: number;
};
export function useCachedQuery<T>(props: CachedQueryProps<T>): CachedQueryResult<T> {
const now = new Date().getTime();
const lowestAllowedTime = now - (props.staleTime ?? defaultStaleTime);
const item = window.localStorage.getItem(props.path);
const result = (item && (JSON.parse(item) as CachedObject<T> | null)) || null;
const refresh = !result || result.refreshTime < lowestAllowedTime || !!props.forceRefresh;
const query = useQuery<T, HttpError>(props.path, () => get<T>(props.path), { ...props.options, enabled: refresh });
if (query.isSuccess) {
const newData = { data: query.data, refreshTime: now };
localStorage.setItem(props.path, JSON.stringify(newData));
return query;
}
return { data: result?.data, isLoading: false, error: null };
}