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

Lodash delay方法在React函数组件内无法读取最新state的原因是什么

问题原因与解决方案

核心原因:函数组件的闭包捕获特性

你遇到的是React函数组件非常典型的闭包陷阱问题,和lodash的delay本身无关——delay本质就是对原生setTimeout的封装,你换成原生setTimeout也会出现相同现象:

  • 你写的useEffect依赖数组为空,所以只会在组件首次挂载时执行1次
  • 首次执行时,testFunction是在首次渲染的作用域中创建的,闭包捕获到的progress值固定为初始值0
  • 后续你通过delay调度的3次调用,用的都是首次渲染生成的旧版testFunction,所以永远只能读到初始的0。即便后续progress更新触发组件重渲染,生成了捕获到新state的新版testFunction,也不会被用到,因为useEffect没有重新执行。

类组件正常工作的原因

类组件的state是挂载在组件实例this上的:

  • 你调用testFunction时读取的是this.state.progress,每次都会访问当前实例上最新的state值
  • 不存在闭包捕获固定值的问题,所以不管什么时候调度调用,都能读到更新后的state。

可行的解决方案

最常用的方案是用useRef存储最新的state值,useRef的current属性是可变的,不会被闭包捕获固定值:

import React, { useEffect, useState, useRef } from 'react';
import { delay } from 'lodash';

const App = () => {
  const [progress, setProgress] = useState(0);
  // 用ref存储最新的progress值
  const progressRef = useRef(progress);

  console.log(progress);

  const testFunction = () => {
    // 读取ref的current属性拿到最新值
    console.log('inside function', progressRef.current);
  };

  useEffect(() => {
    const seconds = 40;
    const attempts = 3;
    const milliseconds = 4000;

    const timer = setInterval(() => {
      setProgress((prevProgress) => {
        const newProgress = prevProgress >= 100 ? 100 : prevProgress + 1;
        // 更新state的同时同步更新ref的值
        progressRef.current = newProgress;
        return newProgress;
      });
    }, seconds * attempts * 10);

    for (let i = 0; i < attempts; i++) {
      delay(testFunction, milliseconds * (i + 1));
    }
    // 组件卸载时清除定时器,避免内存泄漏
    return () => clearInterval(timer);
  }, []);
  return <div>Hello World</div>;
};

export default App;

其他可选方案:

  • 把progress加入useEffect的依赖数组,每次progress更新都重新生成testFunction并调度,需要注意做好去重逻辑,避免重复调度多次
  • 用useCallback包装testFunction,再用useRef存储最新的testFunction实例,调度时读取ref中的最新函数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 03:57:02