通过回调更新父组件状态后,无法更新子组件状态该如何解决?
解决React Native中子组件依次更新父组件与自身状态的问题
我来帮你搞定这个问题!你的核心困扰在于React的setState是异步操作,而且当前的代码没把父子组件的状态更新逻辑关联起来,导致没法按预期依次执行。咱们一步步来修复:
先看修复后的完整代码
export default class GameScreen extends React.Component { constructor(props) { super(props); this.state = { nextOne: 'X' }; this.nextOneCallback = this.nextOneCallback.bind(this); } nextOneCallback() { this.setState(prevState => ({ // 切换当前玩家:X变Y,Y变X nextOne: prevState.nextOne === 'X' ? 'Y' : 'X' })); } render() { const ticTacToeSquare = []; for (let i = 0; i < 3; i++) { const rowArray = []; for (let j = 0; j < 3; j++) { // 把当前玩家状态传递给子组件,同时给每个格子一个稳定的key const newBox = ( <TicTacToeBox key={`${i}-${j}`} callback={this.nextOneCallback} currentPlayer={this.state.nextOne} /> ); rowArray.push(newBox); } const eachRenderedRow = ( <View key={i} style={{ flexDirection: 'row' }}> {rowArray} </View> ); ticTacToeSquare.push(eachRenderedRow); } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> {ticTacToeSquare} <Button title="Home" onPress={() => this.props.navigation.navigate('Home')} /> </View> ); } } class TicTacToeBox extends React.Component { constructor(props) { super(props); this.state = { text: '' }; this.handlePress = this.handlePress.bind(this); } handlePress() { // 先判断格子是否已被填充,避免重复点击 if (this.state.text !== '') return; // 第一步:用父组件传过来的当前玩家更新子组件状态 this.setState({ text: this.props.currentPlayer }, () => { // 第二步:等子组件状态更新完成后,再触发父组件的状态切换 this.props.callback(); }); } render() { return ( <View style={{ borderColor: 'black', borderWidth: 2, alignItems: 'center', justifyContent: 'center' }}> <Text style={{ textAlign: 'center', height: 40, width: 40 }} maxLength={1} onPress={this.handlePress} > {this.state.text} </Text> </View> ); } }
关键修改点解释
- 利用
setState的回调控制顺序:setState支持第二个参数,这是一个在状态更新完成、组件重新渲染后才会执行的回调函数。咱们用它来保证:先把子组件的格子内容更新好,再触发父组件切换下一个玩家,完美解决顺序问题。 - 把父组件状态传给子组件:我给子组件加了
currentPlayerprops,把父组件的nextOne传过去,这样子组件不用硬写'X',而是根据当前玩家来设置文本,完全符合井字棋的轮流落子逻辑。 - 替换不稳定的key:把原来的
Math.random()换成了行列索引组合的key,这样每个格子的key是固定的,不会每次渲染都生成新key导致组件反复卸载挂载,性能更好。 - 添加防重复点击:在子组件点击事件里先检查格子是否已有内容,防止玩家重复点击同一个格子覆盖已落子的内容。
为啥原来的代码不行?
原来的代码里,如果你同时打开this.props.callback()和子组件的setState,因为setState是异步的,父子组件的状态更新可能乱序执行;而且子组件硬编码设成'X',根本没和父组件的状态关联,逻辑完全不对。现在通过setState的回调控制顺序,再让子组件依赖父组件的props,就彻底解决问题啦!
内容的提问来源于stack exchange,提问作者Sreekar Reddy
相关产品推荐
相关产品推荐

