You need to enable JavaScript to run this app. useCallback内的昂贵函数在数据无变更时仍被调用问题排查
优惠活动
大模型
产品
解决方案
定价
更多

useCallback内的昂贵函数在数据无变更时仍被调用问题排查

这种情况完全可能发生,核心原因有以下几点:

  • 你混淆了useCallback的作用:useCallback仅用于缓存函数的引用,避免不必要的子组件重渲染,完全不会阻止函数本身被调用。只要onSuccess触发,不管useCallback返回的函数引用有没有变化,内部的昂贵函数都会执行。
  • React Query的onSuccess回调触发规则:无论数据是从新请求返回还是从本地缓存命中,只要查询成功拿到数据,onSuccess就会执行。你每次访问页面相当于组件重新挂载,useQuery会触发查询流程,哪怕返回的数据和之前完全相同,onSuccess也会被调用,自然会执行昂贵函数。
  • 代码存在变量声明顺序错误:你先在useDataQuery的参数中引用了callbackMemo,之后才用const声明callbackMemo,const存在暂时性死区,首次渲染时直接访问未声明的变量会抛出引用错误,即使运行时没有报错,首次传入的回调也是无效值。
  • useDataQuery的queryKey配置错误:当前的queryKey没有包含参数id,如果不同的id调用该Hook,会命中错误的缓存,也可能导致非预期的回调触发。

修复方案

你完全不需要把回调传给onSuccess,直接通过useEffect监听useQuery返回的data即可,React会对依赖项做引用对比,只有data的引用发生变化时才会执行副作用逻辑:

const [currentData, currentDataSet] = useState({}) 
// 无需传递回调
const dataQuery = useDataQuery(id)

// 仅当data引用变化时执行
useEffect(() => {
  if (!dataQuery.data) return
  const updatedData = expensiveFunction(dataQuery.data)
  currentDataSet(updatedData)
}, [dataQuery.data])

const expensiveFunction = (data) => {
    console.log("Called expensive function")
    let updatedData;
    // 数据处理逻辑
    return updatedData;
}

同时修改useDataQuery的queryKey,把id加入缓存键,避免不同id命中错误缓存:

export const useDataQuery = (
  id: string,
  onSuccess?: Function
) => {
  return useQuery({
    // 把id加入queryKey,不同id对应不同缓存
    queryKey: [queryKeys.app.data, id],
    queryFn: async () =>
      await getData(
        queryKeys.app.data,
        id
      ),
    onSuccess: (data) => {
      onSuccess && onSuccess(data);
    },
  });
};

如果你的后端返回的数据内容相同但引用每次都变,可以在useEffect中加入深对比逻辑,确认内容确实变化后再执行昂贵函数。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 20:54:02