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

React+GraphQL多API调用优化:减少useEffect与组件重渲染

React+GraphQL组件优化:减少useEffect与重渲染

你的场景与问题

你在React+GraphQL项目中未使用Redux这类状态管理工具,把API调用封装在了自定义Hook里,但组件内需要写大量useEffect来触发请求和更新状态,担心过度使用useEffect导致重渲染过多,想知道把请求逻辑抽成独立函数后放进单个useEffect是不是更优的实践。

先看你的现有代码

自定义Hook代码

// 注:原代码存在函数定义不完整问题,推测完整结构如下
function useGetSomeData1() {
  const [getSomeData, { data: getSomeDataData, loading: getSomeDataLoading, error: getSomeDataError }] = useLazyQuery(
    GET_SOME_DATA2,
    {
      client: dataClient,
      fetchPolicy: "cache-and-network",
      nextFetchPolicy: "cache-first",
    }
  );
  return {
    getSomeData,
    getSomeDataData,
    getSomeDataLoading,
    getSomeDataError,
  };
}

export default function useGetSomeData() {
  const [getSomeData2, { data: getSomeDataData2, loading: getSomeDataLoading2, error: getSomeDataError2 }] = useLazyQuery(
    GET_SOME_DATA,
    {
      client: dataClient,
      fetchPolicy: "cache-and-network",
      nextFetchPolicy: "cache-first",
    }
  );
  return {
    getSomeData2,
    getSomeDataData2,
    getSomeDataLoading2,
    getSomeDataError2,
  };
}

组件内原使用方式

useEffect(()=>{
  getSomeData({ variables })
},[])
useEffect(()=>{
  getSomeData2({ variables })
},[])

useEffect(()=>{
  if( getSomeDataData2 && !getSomeDataLoading2 && !getSomeDataError2){
    setState(getSomeDataData2?.data)
  }
},[getSomeDataData2 ,getSomeDataLoading2 ,getSomeDataError2])

useEffect(()=>{
  if( getSomeDataData && !getSomeDataLoading && !getSomeDataError){
    setState(getSomeDataData2?.data) // 注:这里疑似笔误,应该是getSomeDataData?.data
  }
},[getSomeDataData ,getSomeDataLoading ,getSomeDataError])

你设想的优化方式

const fetchData = () => {
  getSomeData({ variables })
}

const fetchData2 = () => {
  getSomeData2({ variables })
}

useEffect(()=>{
  fetchData()
  fetchData2()
},[])

useEffect(()=>{
  if( getSomeDataData2 && !getSomeDataLoading2 && !getSomeDataError2){
    setState(getSomeDataData2?.data)
  }
},[getSomeDataData2 ,getSomeDataLoading2 ,getSomeDataError2])

useEffect(()=>{
  if( getSomeDataData && !getSomeDataLoading && !getSomeDataError){
    setState(getSomeDataData2?.data) // 同样疑似笔误
  }
},[getSomeDataData ,getSomeDataLoading ,getSomeDataError])

优化方案解答

1. 单个useEffect触发多个请求确实更优

把多个初始化请求合并到同一个空依赖useEffect里,不会增加重渲染次数,反而能减少组件挂载时的Effect执行次数(从2次变成1次),代码也更整洁。这种做法是React组件初始化逻辑的常规优化,完全没问题。

2. 进一步减少重渲染的建议

(1)先修复笔误,避免无效状态更新

你的状态更新逻辑里有明显笔误:setState(getSomeDataData2?.data)应该对应getSomeDataData?.data,错误的赋值会导致不必要的状态更新,进而触发无意义的重渲染,先把这个问题解决。

(2)把状态更新逻辑内聚到自定义Hook中

既然已经封装了自定义Hook,完全可以把数据到组件状态的映射逻辑也放进Hook里,组件直接拿到处理好的数据,不用再写额外的useEffect:

// 优化后的自定义Hook
function useGetSomeData1() {
  const [processedData, setProcessedData] = useState(null);
  const [getSomeData, { data: getSomeDataData, loading: getSomeDataLoading, error: getSomeDataError }] = useLazyQuery(
    GET_SOME_DATA2,
    {
      client: dataClient,
      fetchPolicy: "cache-and-network",
      nextFetchPolicy: "cache-first",
      onCompleted: (data) => {
        // 直接在请求完成时更新处理后的数据
        if (data) setProcessedData(data?.data);
      },
      onError: () => {
        // 错误处理可在这里统一处理,比如提示用户
      }
    }
  );

  return {
    getSomeData,
    data: processedData,
    loading: getSomeDataLoading,
    error: getSomeDataError,
  };
}

// 同理优化useGetSomeData
export default function useGetSomeData() {
  const [processedData, setProcessedData] = useState(null);
  const [getSomeData2, { data: getSomeDataData2, loading: getSomeDataLoading2, error: getSomeDataError2 }] = useLazyQuery(
    GET_SOME_DATA,
    {
      client: dataClient,
      fetchPolicy: "cache-and-network",
      nextFetchPolicy: "cache-first",
      onCompleted: (data) => {
        if (data) setProcessedData(data?.data);
      }
    }
  );
  return {
    getSomeData2,
    data: processedData,
    loading: getSomeDataLoading2,
    error: getSomeDataError2,
  };
}

这样组件里只需要调用请求函数,直接使用Hook返回的data即可,完全省去了状态更新的useEffect:

const { getSomeData, data: data1, loading: loading1, error: error1 } = useGetSomeData1();
const { getSomeData2, data: data2, loading: loading2, error: error2 } = useGetSomeData();

useEffect(() => {
  getSomeData({ variables });
  getSomeData2({ variables });
}, []);

// 直接使用data1和data2,不用再手动setState

(3)用useMemo缓存计算结果

如果组件中需要基于返回的数据做计算,可以用useMemo缓存计算结果,避免每次渲染都重新计算:

const combinedData = useMemo(() => {
  if (!data1 || !data2) return null;
  // 基于data1和data2的合并/计算逻辑
  return [...data1, ...data2];
}, [data1, data2]);

总结

  • 把多个初始化请求合并到单个useEffect里是靠谱的优化,既能减少挂载时的Effect执行次数,代码也更清爽。
  • 更核心的优化是把状态更新逻辑塞进自定义Hook里,直接在Hook里处理数据到状态的映射,组件里就不用再写一堆状态更新的Effect了,从根源减少重渲染的触发。
  • 记得先把代码里的笔误改了,不然错误的状态更新会平白无故引发重渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 20:52:09