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

React进度条中useEffect内定时器无法获取最新状态问题

React进度条定时器无法获取最新状态的解决方案

你遇到的是React中常见的闭包陷阱问题:useEffect创建的定时器回调会捕获初始渲染时的state快照,即便后续state更新,定时器里拿到的还是旧值,频繁点击重置后,新旧定时器的状态冲突就会暴露这个问题。

下面是两种可靠的解决方式:

方案一:使用函数式更新修改状态

React的setState支持传入函数,函数参数会自动获取最新的state值,完全避开闭包带来的旧值引用问题:

import { useState, useEffect, useRef } from 'react';

function ProgressBar() {
  const [progress, setProgress] = useState(0);
  const timerRef = useRef(null);

  // 启动定时器逻辑
  const startTimer = () => {
    timerRef.current = setInterval(() => {
      setProgress(prevProgress => {
        if (prevProgress >= 100) {
          clearInterval(timerRef.current);
          return 100;
        }
        return prevProgress + 10;
      });
    }, 1000);
  };

  // 初始启动定时器
  useEffect(() => {
    startTimer();
    return () => clearInterval(timerRef.current);
  }, []);

  // 重置按钮逻辑
  const handleReset = () => {
    clearInterval(timerRef.current);
    setProgress(0);
    startTimer();
  };

  return (
    <div>
      <div style={{ width: `${progress}%`, height: '20px', backgroundColor: 'blue' }}></div>
      <button onClick={handleReset}>重置</button>
    </div>
  );
}

方案二:用useRef保存最新状态

把进度值同步到useRef中,定时器直接访问ref.current获取最新状态,不受闭包影响:

import { useState, useEffect, useRef } from 'react';

function ProgressBar() {
  const [progress, setProgress] = useState(0);
  const progressRef = useRef(0);
  const timerRef = useRef(null);

  // 同步state到ref,确保ref始终持有最新进度值
  useEffect(() => {
    progressRef.current = progress;
  }, [progress]);

  const startTimer = () => {
    timerRef.current = setInterval(() => {
      if (progressRef.current >= 100) {
        clearInterval(timerRef.current);
        return;
      }
      const newProgress = progressRef.current + 10;
      progressRef.current = newProgress;
      setProgress(newProgress);
    }, 1000);
  };

  useEffect(() => {
    startTimer();
    return () => clearInterval(timerRef.current);
  }, []);

  const handleReset = () => {
    clearInterval(timerRef.current);
    progressRef.current = 0;
    setProgress(0);
    startTimer();
  };

  return (
    <div>
      <div style={{ width: `${progress}%`, height: '20px', backgroundColor: 'blue' }}></div>
      <button onClick={handleReset}>重置</button>
    </div>
  );
}

这两种方案都能解决定时器获取旧状态的问题,其中方案一更简洁,推荐优先使用函数式更新的方式。

内容的提问来源于stack exchange,提问作者Laxmi Prasanna Rokkam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:15:06