You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TanStack Query Auth上下文登录/登出后UI不更新问题求助

解决TanStack Query登录/登出后UI不更新的问题

核心问题分析

登出后调用invalidateQueries会触发fetchUser重新请求,但此时用户已登出,请求会失败。TanStack Query默认会保留之前的缓存数据,导致data仍为登录状态的用户信息,isLogged始终为true,UI无法同步更新。登录时同时调用setQueryData和invalidateQueries会导致重复请求,甚至可能因请求失败覆盖正确的缓存数据。

具体修复方案

  1. 替换全局queryClient为组件内钩子获取
    在组件内使用useQueryClient钩子获取实例,避免全局实例可能导致的状态不同步问题。

  2. 优化登录逻辑
    登录成功后直接用返回的用户数据更新缓存,无需调用invalidateQueries(返回的用户数据已是最新状态)。

  3. 修改登出逻辑
    登出时直接移除用户查询的缓存,而非标记为stale后重新请求,确保data立即变为undefined,isLogged同步更新为false。

  4. 处理未登录时的请求错误
    为useQuery添加错误处理,当未登录导致请求失败时,主动清空缓存数据;同时关闭重试机制,避免无效请求。

修改后的关键代码片段

import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
// ...其他导入

const URL = "http://localhost:3000";

const fetchUser = async () => {
  const response = await axios.get(`${URL}/user/profile`, {
    withCredentials: true,
  });
  return response.data;
};

// 全局创建QueryClient(保持不变)
const queryClient = new QueryClient();

function AuthContextWithQuery({ children }: PropsContext) {
  const [displayError, setDisplayError] = useState<{
    username?: string;
    password?: string;
  }>({});
  const navigate = useNavigate();
  // 组件内获取queryClient实例
  const queryClient = useQueryClient();

  const { data, isLoading } = useQuery<Credentials>({
    queryKey: ["user"],
    queryFn: fetchUser,
    staleTime: 1000 * 60 * 5,
    retry: 0, // 关闭重试,避免未登录时重复请求
    onError: () => {
      // 请求失败时清空缓存,确保isLogged同步为false
      queryClient.setQueryData(["user"], undefined);
    },
  });

  const resetErrors = (delay: number) => {
    setTimeout(() => {
      setDisplayError({});
    }, delay);
  };

  // 登录Mutation
  const loginMutation = useMutation({
    // 修正返回类型为Credentials,确保返回用户数据
    mutationFn: async ({ username, password }: User): Promise<Credentials> => {
      const response = await axios.post(
        `${URL}/user/login`,
        { username, password },
        { withCredentials: true },
      );
      return response.data;
    },
    onSuccess: (response) => {
      // 直接用返回的用户数据更新缓存
      queryClient.setQueryData(["user"], response);
      navigate("/");
    },
    onError: (err) => {
      const error = err as AxiosError<{
        username?: string;
        password?: string;
      }>;

      if (error.response?.status === 400) {
        const errorMessage = error.response?.data;
        setDisplayError({
          username: errorMessage?.username,
          password: errorMessage?.password,
        });
      }
      resetErrors(4000);
    },
  });

  // 登出Mutation
  const logoutMutation = useMutation({
    mutationFn: async (): Promise<void> => {
      await axios.post(`${URL}/user/logout`, {}, { withCredentials: true });
    },
    onSuccess: () => {
      // 移除用户查询的缓存,直接让data变为undefined
      queryClient.removeQueries({ queryKey: ["user"] });
      navigate("/auth");
    },
  });

  // ...注册Mutation保持不变

  return (
    <AuthQueryContext.Provider
      value={{
        data,
        isLoading,
        login: loginMutation.mutateAsync,
        register: registerMutation.mutateAsync,
        logout: logoutMutation.mutateAsync,
        setDisplayError,
        displayError,
        resetErrors,
        isLogged: !!data,
      }}
    >
      {children}
    </AuthQueryContext.Provider>
  );
}

export default AuthContextWithQuery;

额外说明

  • 登录时无需调用invalidateQueries,因为登录接口返回的用户数据已经是最新的,直接更新缓存即可让UI同步。
  • 登出时使用removeQueries彻底清除缓存,比setQueryData(null)更直接,避免后续无效请求影响状态。
  • 关闭useQuery的重试机制,减少未登录时的冗余请求,同时在错误回调中主动清空缓存,确保认证状态正确同步。

内容的提问来源于stack exchange,提问作者Matteo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:47:08