如何在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组件的
isActiveprops依赖于activeNum状态,当activeNum变为nextActiveNum时,对应索引的Circle会自动添加active类,之前的Circle因为isActive变为false,会自动移除active类。 - 完全不需要手动操作DOM,React会自动处理视图更新。
额外注意事项
- 定时器ID要存在组件实例(
this.timer)中,在componentWillUnmount里清理,避免内存泄漏。 - 状态更新如果依赖之前的状态值,要用函数式的
setState(prevState => {...}),保证状态更新的准确性。 - 音频对象可以在构造函数中初始化,避免重复创建。
内容的提问来源于stack exchange,提问作者Hasan
相关产品推荐
相关产品推荐

