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

如何在React类组件中用随机数函数切换Circle组件的类?

原生JS转React:用状态控制Circle组件的闪烁效果

核心思路

React是数据驱动视图,不用像原生JS那样直接操作DOM的classList。你只需要在类组件中维护「当前激活的Circle索引」状态,然后让每个Circle组件根据这个状态自动决定是否添加active类。

完整实现代码

1. Circle组件

import React from 'react';

const Circle = ({ isActive }) => {
  return (
    <div className={`circle ${isActive ? 'active' : ''}`}></div>
  );
};

export default Circle;

2. 主游戏类组件

import React, { Component } from 'react';
import Circle from './Circle';

class Game extends Component {
  constructor(props) {
    super(props);
    // 把原生JS里的变量转为组件状态
    this.state = {
      activeNum: 0,
      pace: 1000,
      rounds: 0,
      gameIsOn: false,
    };
    // 存储定时器ID,用于清理
    this.timer = null;
    // 初始化音频
    this.startSound = new Audio("sounds/starter.wav");
    this.endGameSound = new Audio("sounds/gameover.wav");
    this.click = new Audio("sounds/click.wav");
  }

  // 生成随机数函数
  randomNumber = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  };

  // 获取下一个不同的Circle索引
  newCircle = (activeNum) => {
    let nextActiveNum = this.randomNumber(0, 3);
    if (activeNum !== nextActiveNum) {
      return nextActiveNum;
    } else {
      return this.newCircle(activeNum);
    }
  };

  // 播放开始音效
  playAudio = () => {
    this.startSound.play();
  };

  // 结束游戏
  endGame = () => {
    this.setState({ gameIsOn: false });
    clearTimeout(this.timer);
    this.endGameSound.play();
    // 可在此添加状态重置逻辑
  };

  // 开始/循环游戏逻辑
  startGame = () => {
    const { activeNum } = this.state;
    let nextActiveNum = this.newCircle(activeNum);
    
    // 更新状态:替换原生DOM操作的核心
    this.setState(prevState => ({
      activeNum: nextActiveNum,
      pace: prevState.pace - 10,
      rounds: prevState.rounds + 1,
      gameIsOn: true
    }), () => {
      // 状态更新后设置下一次定时器
      if (this.state.rounds < 2) {
        this.timer = setTimeout(this.startGame, this.state.pace);
      } else {
        this.endGame();
      }
    });
  };

  // 组件卸载时清理定时器,防止内存泄漏
  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer);
    }
  }

  render() {
    const { activeNum, gameIsOn } = this.state;
    return (
      <div className="game-container">
        {/* 控制按钮显示 */}
        <button 
          id="start" 
          style={{ display: gameIsOn ? 'none' : 'initial' }}
          onClick={() => {
            this.playAudio();
            this.startGame();
          }}
        >
          开始游戏
        </button>
        <button 
          id="end" 
          style={{ display: gameIsOn ? 'initial' : 'none' }}
          onClick={this.endGame}
        >
          结束游戏
        </button>

        {/* 渲染4个Circle组件,根据activeNum判断是否激活 */}
        <Circle isActive={activeNum === 0} />
        <Circle isActive={activeNum === 1} />
        <Circle isActive={activeNum === 2} />
        <Circle isActive={activeNum === 3} />
      </div>
    );
  }
}

export default Game;

关键代码解释

你原来的两行原生JS代码:

circles[nextActiveNum].classList.toggle("active");
circles[activeNum].classList.remove("active");

在React里被状态更新替代:

this.setState(prevState => ({
  activeNum: nextActiveNum,
  // 其他状态更新...
}))

原因是:

  • 每个Circle组件的isActive props依赖于activeNum状态,当activeNum变为nextActiveNum时,对应索引的Circle会自动添加active类,之前的Circle因为isActive变为false,会自动移除active类。
  • 完全不需要手动操作DOM,React会自动处理视图更新。

额外注意事项

  • 定时器ID要存在组件实例(this.timer)中,在componentWillUnmount里清理,避免内存泄漏。
  • 状态更新如果依赖之前的状态值,要用函数式的setState(prevState => {...}),保证状态更新的准确性。
  • 音频对象可以在构造函数中初始化,避免重复创建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 15:55:36