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

基于@tanstack/react-query v5:useQuery触发useMutation的无限循环问题

解决TanStack React Query v5中useQuery与useMutation的无限循环问题

问题分析

当前代码的无限循环逻辑链:

  • useQuery请求用户数据返回404 → isError为true
  • 组件渲染时直接调用mutateAsync创建用户
  • mutation成功后调用invalidateQueries触发useQuery重新请求
  • 若useQuery仍返回404(或创建失败),组件重新渲染时再次进入isError分支,重复调用mutate形成循环
  • 组件渲染同步逻辑中直接执行mutate,会导致每次渲染只要isError为true就触发请求,进一步加剧循环

解决方案

1. 用状态控制避免重复触发创建操作

添加状态标记是否已尝试创建用户,仅在首次遇到404时触发mutation:

function App() {
  const userInfo = useGetUserQuery();
  const createUser = useCreateUserMutation();
  const [hasInitiatedCreate, setHasInitiatedCreate] = useState(false);

  // 成功获取用户后重置状态(可选,根据业务需求调整)
  useEffect(() => {
    if (userInfo.isSuccess) {
      sessionStorage.setItem('user', JSON.stringify(userInfo.data));
      setHasInitiatedCreate(false);
    }
  }, [userInfo.isSuccess, userInfo.data]);

  // 仅在404且未尝试过创建时触发mutation
  useEffect(() => {
    const attemptCreate = async () => {
      if (userInfo.isError && !hasInitiatedCreate) {
        setHasInitiatedCreate(true);
        try {
          await createUser.mutateAsync();
        } catch (e) {
          console.error('创建用户失败:', e);
          setHasInitiatedCreate(false); // 创建失败允许重试
        }
      }
    };
    void attemptCreate();
  }, [userInfo.isError, hasInitiatedCreate, createUser]);

  return (
    // 组件内容
  );
}

2. 直接更新Query缓存而非重新请求

mutation成功后,直接将创建的用户数据写入Query缓存,无需触发useQuery重新请求,从根源避免循环:
修改useCreateUserMutation的onSuccess逻辑:

export const useCreateUserMutation = () => {
  const queryClient = useQueryClient();
  const auth = useAuth();
  const user: NewUser = {
    // 属性定义
  };

  return useMutation({
    mutationKey: ['user', 'create', user],
    mutationFn: () => createUser(user),
    onSuccess: (data) => {
      // 直接设置对应Query的缓存数据
      queryClient.setQueryData(
        ['user', 'get', data.cnum],
        data
      );
    },
  });
};

此方式会让useQuery立即获取到新创建的用户数据,isSuccess变为true,不再进入错误分支。

3. 区分404错误与其他错误

仅在404时触发创建操作,其他错误(如网络异常)不执行创建,减少不必要的mutation调用:
修改getUser函数的错误处理:

const getUser = async (cnum: string) => {
  try {
    const { data } = await api.get<UserInfo>(`/users/get/${cnum}`);
    return { data, isFound: true };
  } catch (error) {
    // 仅将404视为"未找到",而非错误
    if (error.response?.status === 404) {
      return { data: null, isFound: false };
    }
    // 其他错误抛出,保持isError状态
    throw error;
  }
};

然后在App组件中判断:

function App() {
  const userInfo = useGetUserQuery();
  const createUser = useCreateUserMutation();
  const [hasInitiatedCreate, setHasInitiatedCreate] = useState(false);

  useEffect(() => {
    const attemptCreate = async () => {
      // 仅在未找到用户(404)且未尝试创建时触发
      if (userInfo.data?.isFound === false && !hasInitiatedCreate) {
        setHasInitiatedCreate(true);
        try {
          await createUser.mutateAsync();
        } catch (e) {
          console.error('创建用户失败:', e);
          setHasInitiatedCreate(false);
        }
      }
    };
    void attemptCreate();
  }, [userInfo.data, hasInitiatedCreate, createUser]);

  // 成功获取用户时的逻辑
  useEffect(() => {
    if (userInfo.data?.isFound) {
      sessionStorage.setItem('user', JSON.stringify(userInfo.data.data));
    }
  }, [userInfo.data]);

  return (
    // 组件内容
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:15:24