父组件State更新未触发重渲染的React技术问题
React父组件更新State后子组件不同步的问题解决
核心问题出在StateSelections组件的错误实现:它在初始化时将父组件传入的props.states存入自身state,后续父组件props更新时,该组件的state不会自动同步,导致渲染的子组件始终使用初始状态,所以父组件调用setState后视图没有更新。
问题原因
React类组件的constructor仅在组件首次挂载时执行一次。StateSelections中state.stateSelections是用初始化时的props.states创建的组件实例,当父组件的states更新后,StateSelections的props发生变化,但它没有响应这个变化去更新自身state,因此渲染的还是旧的组件实例,这些实例持有的states还是初始值。
解决方案
1. 移除StateSelections中冗余的state
不需要将组件元素存储在state里,直接在render方法中使用最新的props渲染子组件即可。这样每次父组件props更新时,StateSelections的render会重新执行,生成使用最新props的子组件实例。
修改后的StateSelections组件:
class StateSelections extends React.Component { render() { return ( <div> <StateSelectionWrapper key={1} states={this.props.states} updateStates={this.props.updateStates}/> <StateSelectionWrapper key={2} states={this.props.states} updateStates={this.props.updateStates}/> </div> ); } }
2. 理解React重渲染与状态同步的核心机制
- 父组件调用
setState后,自身会触发重渲染,同时默认会触发所有子组件的重渲染(除非子组件做了性能优化)。 - 子组件接收的props更新时,会触发重渲染,从而使用最新的props渲染视图。
- 除非有特殊业务需求,否则不要将父组件的props存入子组件的state中——直接使用props就能保证数据与父组件同步。
3. 多子组件共享状态的正确方式
你的核心思路是正确的:将共享状态提升到所有子组件的共同父组件(App)中,子组件通过props获取状态,通过props传入的回调函数更新父组件状态。所有依赖该状态的子组件,只要直接从props读取最新值,就能保证状态同步更新。
修改后的完整代码
class App extends React.Component { initState() { let states = {} for(let i = 0 ; i < 5; i++) { states[i] = { selectedStatus: 'unselected'} } return states; } constructor(props) { super(props); this.state = { states: this.initState() }; this.updateStates = this.updateStates.bind(this); } updateStates(s) { this.setState({ states: s }) } render() { return ( <div> <StateSelections states={this.state.states} updateStates={this.updateStates}/> </div> ); } } class StateSelections extends React.Component { render() { return ( <div> <StateSelectionWrapper key={1} states={this.props.states} updateStates={this.props.updateStates}/> <StateSelectionWrapper key={2} states={this.props.states} updateStates={this.props.updateStates}/> </div> ); } } class StateSelectionWrapper extends React.Component { render() { return ( <div> <StateSelection states={this.props.states} updateStates={this.props.updateStates} /> </div> ) } } class StateSelection extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); this.executeCallback = (c) => props.updateStates(c); } handleClick(s) { let copiedState = JSON.parse(JSON.stringify(this.props.states)); if (copiedState[s].selectedStatus === 'unselected') { copiedState[s].selectedStatus = 'selected'; } else { copiedState[s].selectedStatus = 'unselected'; } this.executeCallback(copiedState); } renderState(s) { if (this.props.states == null) { return; } let color = this.props.states[s].selectedStatus === 'unselected' ? 'white' : 'red'; return ( <S key={s} value={s} color={color} onClick={() => this.handleClick(s)} /> ) } render() { return ( <div> {this.renderState(0)} {this.renderState(1)} {this.renderState(2)} {this.renderState(3)} {this.renderState(4)} </div> ); } } function S(props) { return ( <button style={{backgroundColor: props.color}} onClick={props.onClick}>{props.value}</button> ); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <App /> );
内容的提问来源于stack exchange,提问作者Esoteric
相关产品推荐
相关产品推荐

