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

React Query随机重取与重渲染异常问题求助

React Query API随机重取问题排查

项目使用React Query,此前运行正常,近期出现API随机重取的异常,且发现获取通知后页面内所有API都会被重取。


代码片段

通知钩子代码

export const GET_NOTIFICATIONS = "GET_NOTIFICATIONS";
const useNotification = (autoFetch: boolean, query: any) => {
  const REFETCH_EACH = 7000;
  const { httpClient } = useAxios();

  const getNotifications = async () => httpClient?.get<any>(`${NOTIFICATIONS_ENDPOINT}/search`, query);

  return useQuery([GET_NOTIFICATIONS], () => getNotifications(), {
    enabled: autoFetch && httpClient !== undefined,
    refetchInterval: autoFetch ? REFETCH_EACH : false
  });
};

全局上下文提供者代码

interface Props {
  children: ReactNode;
}

const GlobalContextProvider = (props: Props) => {
  const { state, setState } = useSharedContext();
  const { data: referentials, isSuccess, isFetching: referentialsFetching } = useReferentials();
  const { data: notifications, isSuccess: notificationsSuccess } = useNotification(true, {
    status: notificationStatus.PENDING.status,
    page: 0,
    size: 100
  });
  const { data: rbac, isSuccess: rbacSuccess, isFetching: rbacLoading } = useRoleBased();

  useEffect(() => {
    if (isSuccess && setState) {
      setState((prev) => ({
        ...prev,
        referentials
      }));
    }
  }, [referentials, isSuccess, setState]);

  useEffect(() => {
    if (notificationsSuccess && setState) {
      setState((prev) => ({
        ...prev,
        notifications
      }));
    }
  }, [notifications, notificationsSuccess]);

  useEffect(() => {
    if (rbacSuccess && setState) {
      setState((prev) => ({
        ...prev,
        rbac,
      }));
    }
  }, [rbac, rbacSuccess, setState]);

  if (rbacLoading || referentialsFetching) {
    return <FullPageLoader title="Chargement en cours..." />;
  }

  return <GlobalContext.Provider value={{ state, setState }}>{props.children}</GlobalContext.Provider>;
};

export default GlobalContextProvider;

被随机重取的API示例代码

const usePreRegulationKpi = () => {
  const { httpClient } = useAxios();

  const getPreRegulationKpi = async () => httpClient?.get<any>(`${INTERMEDIATE_ENDPOINT}/kpi`);

  return useQuery([GET_REGULATION_KPI], () => getPreRegulationKpi(), {
    enabled: false
  });
};

// 调用示例:
const {
  refetch: refetchPreRegulationKpi,
  isFetching: regulationKPIsLoading,
  isSuccess: regulationKPIsSuccess,
  isError: regulationKPIsError,
  data: regulationKPIsData
} = usePreRegulationKpi();

React Query全局配置代码

import { QueryClient, QueryClientConfig } from "@tanstack/react-query";

const queryClientConfig: QueryClientConfig = {
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      retry: false
    }
  }
};

export const queryClient = new QueryClient(queryClientConfig);

问题原因分析

1. 全局上下文Provider的条件渲染触发子组件卸载/重挂载

GlobalContextProvider在rbacLoading或referentialsFetching为true时返回FullPageLoader,直接替换子组件渲染,导致子组件被卸载。当通知查询完成更新全局state后,若useRoleBased或useReferentials的查询依赖state值并触发重取,会使rbacLoading/referentialsFetching再次变为true,触发Loader渲染;查询完成后子组件重新挂载,内部所有useQuery会重新初始化,若组件挂载时存在自动触发的查询(或useEffect中调用refetch),就会导致API批量重取。

2. 通知查询的Query Key未包含动态参数

useNotification的query key仅使用[GET_NOTIFICATIONS],未将传入的query参数纳入其中。若query参数发生变化,React Query会判定为同一查询,可能引发缓存混乱,间接触发其他查询的异常重取。

3. 全局state更新导致子组件频繁重渲染

通知更新触发全局state修改后,所有消费GlobalContext的子组件会重渲染。若子组件中的useQuery依赖state值(如query key包含state),或在useEffect中以state为依赖调用refetch,都会触发对应API的重取。


解决方案

1. 修复全局Provider的条件渲染逻辑

避免通过条件渲染卸载子组件,改为在Provider内部控制Loader的显示:

const GlobalContextProvider = (props: Props) => {
  // ... 保留原有逻辑

  return (
    <GlobalContext.Provider value={{ state, setState }}>
      {rbacLoading || referentialsFetching ? (
        <FullPageLoader title="Chargement en cours..." />
      ) : (
        props.children
      )}
    </GlobalContext.Provider>
  );
};

此方式下子组件不会被卸载,仅被隐藏,避免重挂载导致的查询重执行。

2. 完善通知查询的Query Key

将动态参数query加入query key,确保不同参数对应独立的缓存条目:

return useQuery([GET_NOTIFICATIONS, query], () => getNotifications(), {
  enabled: autoFetch && httpClient !== undefined,
  refetchInterval: autoFetch ? REFETCH_EACH : false
});

3. 优化子组件对全局Context的依赖

  • 避免在useEffect中以全局state为依赖调用refetch,除非业务确有需要;
  • 若必须依赖,使用useCallback包装refetch方法,减少不必要的触发;
  • 确保useQuery的query key仅包含必要依赖,避免因无关state变化触发重取。

4. 排查useRoleBased和useReferentials的查询逻辑

检查这两个查询是否依赖全局state,若存在不必要的依赖,调整query key或移除依赖;若确需依赖state,确保查询重取不会触发子组件卸载。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:04:51