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

当useEffect与useCallback依赖相同时,二者的执行行为是怎样的?

React useCallback与useEffect同依赖时的执行行为分析

示例代码

const [accountId, setAccountId] = useState(null);
// accountId gets set in code not shown here

const makeAsyncNetworkCall = useCallback(async () => {
  const response = await get(buildUrl(url, accountId));

  if (response.ok) {
    return response.data;
  }

  return [];
}, [accountId]);

useEffect(() => {
  if (accountId) {
    // do some extra work here
  }

  makeAsyncNetworkCall()
      .then(response => {
        // handle response
      })
      .catch(() => {
        // handle error
      });
}, [accountId]);

问题描述

已知当useEffect的依赖项accountId变化时,其回调函数会执行。但改用useCallback包裹异步函数后,疑惑当useEffect调用拥有相同依赖项的useCallback函数时,二者的执行行为是怎样的?
猜想如下:当useCallback的依赖项变化时,它会重新创建函数并捕获内部变量(使用useCallback前曾遇到状态滞后问题);useEffect会因依赖项变化触发调用useCallback函数。想确认该猜想是否正确,以及执行顺序是否会影响结果。

解答

你的猜想完全正确,执行顺序也不会导致意外结果,具体执行流程如下:

  • 当accountId发生变化时,React首先更新state中的accountId值
  • 组件进入重新渲染阶段,此时useCallback检测到依赖项accountId已变更,会重新创建makeAsyncNetworkCall函数,这个新函数会捕获最新的accountId值
  • 组件渲染完成后,进入useEffect执行阶段,由于accountId是useEffect的依赖项,其回调函数会被触发
  • 在useEffect回调中调用的是本次渲染生成的最新版本的makeAsyncNetworkCall函数,因此内部使用的accountId是最新的,不会出现之前遇到的状态滞后问题

执行顺序的说明

useCallback的函数更新发生在组件渲染阶段,而useEffect的回调执行发生在组件渲染完成后的commit阶段之后。由于两者的依赖完全一致(都是accountId),每次accountId变化时,必然是先更新makeAsyncNetworkCall函数,再执行useEffect回调并调用这个新函数,不会出现调用旧版本函数的情况。

为什么不用useCallback会有状态滞后?

如果不用useCallback包裹makeAsyncNetworkCall,每次组件渲染都会生成一个新的函数实例。如果useEffect的依赖项没有正确包含这个函数(或者依赖配置有误),就可能导致useEffect捕获到旧版本的函数,其中的accountId还是更新前的旧值,从而出现状态滞后问题。而useCallback确保只有当accountId变化时才生成新函数,结合useEffect对accountId的依赖,完美规避了这个问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 21:27:41