React开发Lights Out游戏时子组件向父组件更新数组数据混乱如何解决
问题根源
- 子组件点击逻辑无状态校验:
clickHandler触发时,未判断当前灯的亮灭状态,无论操作是点亮还是熄灭,都直接调用父组件方法追加序号,导致数组仅持续新增内容,和实际亮灯情况不匹配。 - 父组件同步方法逻辑缺失:现有
showNum方法仅支持向数组追加内容,没有实现灯熄灭时移除对应序号的逻辑。 - 初始化逻辑存在副作用风险:子组件
rand方法内部直接调用父组件传值方法,React 18 严格模式下组件会二次挂载初始化,会导致初始亮灯序号重复插入数组。
修复方案
调整父子组件逻辑,让父组件的lightNums数组始终和当前亮灯序号一一对应,亮灯新增、灭灯移除:
父组件修改
将原本仅支持追加的showNum方法调整为支持新增/移除的切换方法,自动判断序号是否存在于数组中做对应操作:
import "./App.css"; import Square from "./Square"; import React, { Component } from "react"; class App extends Component { constructor(props) { super(props); this.state = { lightNums: [], }; } // 支持亮灯新增、灭灯移除的同步方法 toggleLightNum(num) { this.setState((prevState) => { const { lightNums } = prevState; return lightNums.includes(num) ? { lightNums: lightNums.filter(item => item !== num) } : { lightNums: [...lightNums, num] }; }); } arr = Array.from({ length: 25 }).map((e, idx) => { return <Square num={idx} key={idx} toggleLightNum={this.toggleLightNum.bind(this)} />; }); render() { return <div className="App">{this.arr}</div>; } } export default App;
子组件修改
移除rand方法内的父组件调用,把初始化同步逻辑放到构造函数内,避免重复执行;点击时无需额外判断,直接调用父组件切换方法即可:
import React, { Component } from "react"; import "./Square.css"; class Square extends Component { constructor(props) { super(props); this.clickHandler = this.clickHandler.bind(this); // 初始化随机亮灯状态 const initClicked = this.getRandomInitState(); this.state = { clicked: initClicked, }; // 初始状态为亮灯时,同步到父组件 if (initClicked) { this.props.toggleLightNum(this.props.num); } } clickHandler() { this.setState((prevState) => { const newState = !prevState.clicked; // 状态更新后同步到父组件 this.props.toggleLightNum(this.props.num); return { clicked: newState }; }); } getRandomInitState() { return Math.floor(Math.random() * 2) === 1; } classN = () => `square ${this.state.clicked ? "clicked" : ""}`; render() { return ( <div className={this.classN()} onClick={this.clickHandler}> {this.props.num} </div> ); } } export default Square;
内容的提问来源于stack exchange,提问作者Amit Hagag
相关产品推荐
相关产品推荐

