【问题标题】:ReactQuery: use multiple QueryClients with different settings?React Query:使用具有不同设置的多个查询客户端?
【发布时间】:2021-07-04 02:03:29
【问题描述】:

我有一个 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 的响应放入本地存储?如果是这样,我该如何处理失效?

【问题讨论】:

    标签: javascript reactjs typescript react-query


    【解决方案1】:

    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,它在实际中使用(我从我的博客中获取了这个:https://tkdodo.eu/blog/testing-react-query)。不确定这是否能帮助您解决persistQueryClient 问题,但如果您只想更改componentTree 某些部分的cacheTime,就可以了。

    【讨论】:

    • 不知道为什么,但它并没有真正起作用。当我打开浏览器的开发者工具时,我可以看到本地存储中的数据。但是,当我按 F5 时,查询仍然调用后端 API,我可以看到时间戳增加。
    • 我认为你在这里搞混了。您询问了cacheTime,但您是否真的想自定义staleTime?看起来您的组件已挂载,您从缓存中正确获取数据,然后由于默认的 staleTime0 而获得后台重新获取。
    • 即使同时定义了cacheTimestaleTime,每次F5 刷新都会更新本地存储。我不确定我必须定义哪些选项。我要问的是:“ 我想将稳定的数据存储在浏览器的本地存储中,以减少对服务器的请求数量(从而使 UI 应用程序更快一些)和每 24 小时刷新一次。但是,我想在每次请求时使易失性数据失效并重新获取,因为其他用户可能同时更改了数据。 "
    【解决方案2】:

    最终,我决定使用本地存储创建自己的钩子。这很简单,我可以重复使用默认的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 };
    }
    

    【讨论】:

      猜你喜欢
      • 2021-05-17
      • 2022-08-08
      • 2020-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-17
      • 2020-03-22
      • 2021-08-12
      相关资源
      最近更新 更多