防止 useSWR 在 mutate() 调用之前获取数据

11
我有一个React应用程序,它使用SWR + Axios进行数据获取(https://swr.vercel.app/docs/data-fetching)。问题在于,我的自定义hook使用useSwr时,每当初始化钩子时都会获取所有数据。我的目标是仅在调用mutate时获取数据。目前即使没有调用mutate,初始获取也会发生。如何实现我的目标?
我的应用程序包含在SWRConfig中:
    <SWRConfig
      value={{
        fetcher,
      }}
    >
      <App/>
    </SWRConfig>

抓取器被描述为:
const dataFetch = (url) => axios.get(url).then((res) => res.data);

function fetcher(...urls: string[]) {
  if (urls.length > 1) {
    return Promise.all(urls.map(dataFetch));
  }
  return dataFetch(urls);
}

使用useSwr的自定义钩子

import useSWR, { useSWRConfig } from "swr";

export function useCars(registrationPlates: number[]): ICars {
  const { mutate } = useSWRConfig();
  const { data: carData} = useSWR<Car[]>(
    carsToFetchUrls(registrationPlates),  // returns string array with urls to fetch
    {
      revalidateOnFocus: false,    
      revalidateOnMount: false,
      revalidateOnReconnect: false,
      refreshWhenOffline: false,
      refreshWhenHidden: false,
      refreshInterval: 0,
    }
  );

  const getCar = (
    carRegistrationPlate: number,
  ): Car => {
    console.log(carData) // carData contains data from fetch even before calling mutate()

    void mutate();
    
    ...
}

用法:(这将位于希望使用useCars钩子的某个组件中)

const { getCar } = useCars(carsRegistrationPlates);

目前即使没有调用mutate,初始获取仍在进行。通常情况下,当您保存或编辑并希望更新向用户显示的内容以反映所述保存或编辑时,会使用mutate,然后调用mutate来同步缓存数据(与服务器上可用的数据),这将强制swr从服务器重新获取数据。当swr重新获取数据时,本地缓存将得到更新,因此向用户显示的内容也将得到更新,即UI将被刷新。 - Sangeet Agarwal
3个回答

9
你可以在 useSWR 调用中使用条件获取来防止它发起请求。
来自 useSWR条件获取文档:
使用 null 或将函数作为key传递以有条件地获取数据。如果该函数抛出异常或返回一个 falsy 值,SWR 将不会发起请求。
export function useCars(registrationPlates: number[], shouldFetch): ICars {
    const { data: carData} = useSWR<Car[]>(
        shouldFetch ? carsToFetchUrls(registrationPlates) : null,
        { // Options here }
    );
    // ...
    return { carData, /**/ }
}

你可以按照以下步骤使用它,以避免进行初始请求。

const [shouldFetch, setShouldFetch] = useState(false);
const { carData } = useCars(carsRegistrationPlates, shouldFetch);

接着,当您想发起请求的时候,只需将 shouldFetch 设置为 true 即可。

setShouldFetch(true)

谢谢你的回答。这确实会防止预取。但是当调用getCars并包含mutate时,useSwr钩子仍将获取null。 - DLO
只有在使用key值显式调用mutate(carsToFetchUrls(registrationPlates))时才会发生变异。 - juliomalves
也许我没有正确使用 mutate。我尝试过 mutate(carsToFetchUrls(registrationPlates)),但是 carData 仍然未定义。我还尝试使用 async/await 进行获取,但这并没有改变任何事情... - DLO
1
@Deeple 对的,在这种情况下,你不会使用 mutate,而是将 shouldFetch 变量移入状态,并在想要进行变异时将其设置为 true - juliomalves

2

这里是实现您所希望实现的可能方式。我在我的一个生产应用程序中使用了类似的方法。首先创建一个自定义的swr hook,如下所示:

const useCars = (registrationPlates: number[]) => {
  const fetcher = (_: string) => {
    console.log("swr-key=", _);
    return dataFetch(registrationPlates);
  };

  const { data, error, isValidating, revalidate, mutate } = useSWR(`api/car/registration/${JSON.stringify(registrationPlates)}`, fetcher, {
    revalidateOnFocus: false,
  });

  return {
    data,
    error,
    isLoading: !data && !error,
    isValidating,
    revalidate,
    mutate,
  };
};

export { useCars };

现在,您可以从任何其他组件调用此钩子,如下所示:
const { data, error, isLoading, isValidating, revalidate, mutate } = useCars(carsRegistrationPlates);

现在,您可以通过传递给上面的useCars函数来控制返回的内容。 请注意,我们自定义的swr hook中传递给useSwr的第一个参数是关键字,swr使用它来缓存值,如果这个关键字保持不变,那么swr将透明地返回缓存的值。

此外,使用这个自定义hook,您可以获取状态,比如加载中、错误等状态,因此您可以在消费组件中针对每个状态采取适当的措施。


感谢您的回答。这就像我设置的那样。useSwr钩子在调用mutate之前就预取数据了。所以,如果我只是在某个地方使用const up useCars,它就会获取数据。在我的情况下,从该获取中传入了大量数据,因此我只希望在调用“mutate”时才执行此操作。显然,“revalidateOnX:false”对我不起作用。 - DLO
嗨@Deeple,我也遇到了同样的问题。你找到解决方案了吗? - Arun Selva Kumar

0
你可以通过在使用useSwr钩子时传递revalidate选项来执行此操作,如下所示:
useSWR(key, fetcher, {
  revalidateIfStale: false,
  revalidateOnFocus: false,
  revalidateOnReconnect: false
})

或者使用useSWRImmutable如下:

// equivalent to
useSWRImmutable(key, fetcher);

在他们的文档中查看禁用重新验证


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接