为什么我的 react-query 查询在 unmount/mount 操作后重新获取?

Why is my react-query query refetching after an unmount/mount operation?

设置

我有以下 react-query 钩子:

const STALE_TIME_MS = 12 * 60 * 60 * 1000; // 12h

const useUser = () => {
  const services = useServices();
  const { isLoading, error, data } = useQuery(
    'user',
    () => services.user.verify(),
    {
      staleTime: STALE_TIME_MS,
      cacheTime: STALE_TIME_MS,
    }
  );
  return {
    isLoading,
    error,
    user: data,
  };
};

然后,我有一个 App 组件,因为 child 位于 QueryClientProvider:

const App = () => {
  const { isLoading } = useUser();

  if (isLoading) {
    return <Authenticating />;
  }

  return (
    <HashRouter>
      <Switch>
        <Route path="/login" component={Login} />
        <Route path="/" component={Dashboard} />
      </Switch>
    </HashRouter>
  );
}

Login 组件检查用户是否已经登录并将其重定向回 /:

const Login = () => {
  const { user } = useUser();

  if (user) {
    return <Redirect to="/" />;
  }

  ...
}

Dashboard 组件执行相反的操作:

const Dashboard = () => {
  const { user } = useUser();

  if (!user) {
    return <Redirect to="/login" />;
  }

  ...
};

问题

services.user.verify() 发出的请求失败时,react-query 会无限期地重试 user 查询。尽管 react-query 的默认重试次数是 3 次。

问题似乎是因为我从 mounting/unmounting 的多个组件调用 useUser()。当我从 DashboardLogin 中删除重定向逻辑时,react-query 重试 3 次,然后按预期进入登录屏幕。

有趣的是 user 查询被标记为过时。如果我将我的 services.user.verify() 实现更改为 return null 而不是在用户未通过身份验证时抛出错误,查询将被标记为新鲜,我根本没有这个问题。

问题

Does react-query always consider queries stale when they fail even after the retries?

是的。 staleTime 仅针对成功的查询启动,没有数据的查询(例如,因为它们失败了)被认为是陈旧的。如果您的查询成功一次,然后在重新获取时失败,则它不会自动被视为陈旧 - staleTime 现在很重要,因为您已经有数据。

Does a new component calling the query always result in a retry?

当组件挂载时,react-query 触发获取。这是由于标志refetchOnMount,所以如果你不想要的话,你可以自定义这个标志。根据 retry 标志触发重试,是的,如果触发提取,则相应地触发重试。

Is there an option to prevent this from happening? The problem seems to come from the fact that I am calling useUser() from multiple components that are mounting/unmounting.

是的,这似乎是根本原因。您可以使用 refetchOnMount 选项解决此问题,或者,可能更好 - 不要重定向到调用 useUser 的路由(并期望用户在场),除非您已经拥有该数据用户。例如,如果您的 useUser 处于错误状态,您可以安装 Login 组件,因为这意味着您未通过身份验证,并且只有在您有用户时才呈现 Dashboard