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

React中useState调用被合并引发渲染与任务控制异常求助

React Promise进度更新与任务终止问题修复

问题根源

  1. 进度更新异常:循环中调用setState时,始终引用闭包中的旧state值,每次更新都是基于初始的progress(0)计算,最终只会得到progress:1;同时React会批量合并异步的setState调用,导致中间进度无法显示。
  2. 任务无法终止:startWork函数中的state是执行时的快照,后续cancelWork更新的total无法被循环内的判断读取到,终止逻辑失效。

修复代码

import React, { useState, useRef } from "react";

export default function () {
  const [state, setState] = useState({ progress: 0, total: 0 });
  const isCancelledRef = useRef(false);

  const wait = (duration) => () => new Promise((resolve) => setTimeout(resolve, duration));

  const startWork = async () => {
    const tasks = [wait(100), wait(200), wait(300), wait(400), wait(500)];
    isCancelledRef.current = false;

    // 基于最新状态初始化进度
    setState(prev => ({ ...prev, progress: 0, total: tasks.length }));

    for (const task of tasks) {
      // 实时检查取消标记
      if (isCancelledRef.current) break;

      await task();

      // 函数式更新,确保使用最新状态计算进度
      setState(prev => ({ ...prev, progress: prev.progress + 1 }));
    }

    // 取消后重置状态
    if (isCancelledRef.current) {
      setState(prev => ({ ...prev, total: 0, progress: 0 }));
    }
  };

  const cancelWork = () => {
    isCancelledRef.current = true;
    setState(prev => ({ ...prev, total: 0, progress: 0 }));
  };

  return (
    <div>
      <p>
        {state.progress} / {state.total}
      </p>
      <button onClick={startWork}>Start</button>
      <button onClick={cancelWork}>Cancel</button>
    </div>
  );
}

关键修复点

  • 函数式更新setState:用setState(prev => newState)替代直接依赖闭包中的state,确保每次更新都基于最新状态值,解决进度计算错误和批量更新导致的状态不连贯问题。
  • useRef存储取消标记:useRef的current属性在组件生命周期中保持可变且可实时访问,避免闭包无法获取最新state的问题,确保Cancel按钮点击后能立即终止任务循环。
  • 实时检查取消状态:在每次任务执行前检查取消标记,一旦标记为true就跳出循环,终止后续任务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:25:07