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

React中如何在超时后设置state?轮盘组件spinning状态异常

问题原因分析

你的问题出在异步代码的时序逻辑上:

  • setTimeout 是异步任务,循环中所有的setTimeout回调会被加入事件队列,等待当前同步代码执行完毕后才会依次触发。
  • 你在循环结束后立刻调用setSpinning(false),这行同步代码会在所有setTimeout回调执行前就运行,把spinning设为false。但后续每个setTimeout回调里又会调用setSpinning(true),最终导致spinning始终停留在true状态。

另外,直接用document.getElementById修改DOM样式不符合React的设计理念,应该用状态来管理样式属性。

修复后的代码
import React, { useState } from "react";

const numbers = [
  0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24,
  16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26,
];

const green = [0];
const red = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36];
const black = [2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35];

const RouletteWheel = () => {
  const [number, setNumber] = useState(0);
  const [spinning, setSpinning] = useState(false);
  const [numberColor, setNumberColor] = useState("green"); // 新增颜色状态

  const getRandomNumber = () => {
    return numbers[Math.floor(Math.random() * numbers.length)];
  };

  const spin = () => {
    const targetNum = getRandomNumber();
    const totalSteps = numbers.length * 2 + numbers.indexOf(targetNum) + 1;
    
    setSpinning(true); // 一开始就标记为旋转状态

    for (let j = 0; j < totalSteps; j++) {
      setTimeout(() => {
        const currentNum = numbers[j % numbers.length];
        // 根据当前数字设置颜色状态
        if (green.includes(currentNum)) {
          setNumberColor("green");
        } else if (red.includes(currentNum)) {
          setNumberColor("red");
        } else {
          setNumberColor("black");
        }
        setNumber(currentNum);

        // 最后一步执行完成后,结束旋转状态
        if (j === totalSteps - 1) {
          setTimeout(() => setSpinning(false), 0);
        }
      }, j * 1.25); // 调整延迟计算,避免动画等待时间指数级增长
    }
  };

  return (
    <div className="roulette">
      <h1 id="numberDisp" style={{ color: numberColor }}>{number}</h1>
      <button onClick={spin} disabled={spinning}>
        {spinning ? "Spinning..." : "Spin the wheel!"}
      </button>
    </div>
  );
};

export default RouletteWheel;
关键修改点
  1. 修正异步时序:

    • 旋转开始时立刻调用setSpinning(true),明确标记轮盘状态。
    • 在最后一个setTimeout回调中,额外添加延迟任务(即使延迟0也能确保DOM更新完成),将spinning设为false,保证动画完全结束后再更新状态。
  2. 用状态管理样式:

    • 新增numberColor状态控制数字颜色,替代直接操作DOM的方式,符合React单向数据流原则。
  3. 优化动画延迟:

    • 原代码中j * (j * 1.25)会导致延迟时间指数级增长,改成j * 1.25让动画匀速过渡,避免后续步骤等待时间过长。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:40:02