React更新state数组后子组件UI未同步更新的问题排查
问题原因
子组件的expenseData状态仅在组件首次挂载时通过构造函数从props.data初始化,后续父组件更新dataSource导致props.data变化时,子组件没有同步更新state.expenseData的逻辑,因此this.state.expenseData.ValidFlag始终显示初始化时的旧值;而this.props.validFlag直接读取父组件传递的最新props,所以能正常更新。
解决方案
方案1:直接使用Props(推荐)
如果子组件仅需展示数据、不需要独立维护状态,完全可以直接使用this.props.data替代this.state.expenseData,无需将props存入state。
修改后的子组件代码:
import React, { Component } from "react"; import '../../CompCss/ExpenseEditableBlock.css'; import 'react-dropdown/style.css'; import { Utils } from './../Utils'; const utils = Utils(); class ExpenseEditableBlock extends Component { constructor(props){ super(props); this.state = { categoryArray: utils.expCategories().map(item => item.label), _otherVarName_ : null, checked: false } } render() { const { data } = this.props; return ( <div className={(data.ValidFlag === "FAIL")?"exp-block-box-one disabled-item":"exp-block-box-one"} style={this.props.style}> <div id="exp-item-head-section"> <div className="display-inline-block"> <p>Trx ID: {data.TrxID}</p> <p>{data.ValidFlag}</p> {/* 现在会随props更新 */} <p>{this.props.validFlag}</p> </div> </div> <div> <div className="exp-date-block">{data.TrxDate}</div> <div> <p>{data.TrxTitle}</p> </div> </div> <div style={{height: '3px'}}> {/* <TextField id="standard-basic" label="Standard" variant="standard" /> */} </div> </div> ) } } export default ExpenseEditableBlock;
方案2:同步Props到State(当子组件需要独立维护状态时)
如果子组件需要修改expenseData并维护自己的状态,需在props变化时主动同步到state。可以使用componentDidUpdate或getDerivedStateFromProps:
方式1:使用componentDidUpdate
import React, { Component } from "react"; import '../../CompCss/ExpenseEditableBlock.css'; import 'react-dropdown/style.css'; import { Utils } from './../Utils'; const utils = Utils(); class ExpenseEditableBlock extends Component { constructor(props){ super(props); this.state = { expenseData: props.data, categoryArray: utils.expCategories().map(item => item.label), _otherVarName_ : null, checked: false } } // 当props更新时同步state componentDidUpdate(prevProps) { if (prevProps.data !== this.props.data) { this.setState({ expenseData: this.props.data }); } } render() { return ( <div className={(this.state.expenseData.ValidFlag === "FAIL")?"exp-block-box-one disabled-item":"exp-block-box-one"} style={this.props.style}> <div id="exp-item-head-section"> <div className="display-inline-block"> <p>Trx ID: {this.state.expenseData.TrxID}</p> <p>{this.state.expenseData.ValidFlag}</p> {/* 现在会同步更新 */} <p>{this.props.validFlag}</p> </div> </div> <div> <div className="exp-date-block">{this.state.expenseData.TrxDate}</div> <div> <p>{this.state.expenseData.TrxTitle}</p> </div> </div> <div style={{height: '3px'}}> {/* <TextField id="standard-basic" label="Standard" variant="standard" /> */} </div> </div> ) } } export default ExpenseEditableBlock;
方式2:使用getDerivedStateFromProps(更符合React规范)
import React, { Component } from "react"; import '../../CompCss/ExpenseEditableBlock.css'; import 'react-dropdown/style.css'; import { Utils } from './../Utils'; const utils = Utils(); class ExpenseEditableBlock extends Component { constructor(props){ super(props); this.state = { expenseData: props.data, categoryArray: utils.expCategories().map(item => item.label), _otherVarName_ : null, checked: false } } // 静态方法,在props变化时计算新的state static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.data !== prevState.expenseData) { return { expenseData: nextProps.data }; } return null; // 无需更新state时返回null } render() { return ( <div className={(this.state.expenseData.ValidFlag === "FAIL")?"exp-block-box-one disabled-item":"exp-block-box-one"} style={this.props.style}> <div id="exp-item-head-section"> <div className="display-inline-block"> <p>Trx ID: {this.state.expenseData.TrxID}</p> <p>{this.state.expenseData.ValidFlag}</p> {/* 现在会同步更新 */} <p>{this.props.validFlag}</p> </div> </div> <div> <div className="exp-date-block">{this.state.expenseData.TrxDate}</div> <div> <p>{this.state.expenseData.TrxTitle}</p> </div> </div> <div style={{height: '3px'}}> {/* <TextField id="standard-basic" label="Standard" variant="standard" /> */} </div> </div> ) } } export default ExpenseEditableBlock;
额外注意点
- 初始化
categoryArray时,不要直接用this.state.categoryArray.push(...)修改state,应该在构造函数中直接通过map生成新数组赋值,避免直接修改state导致的不可预测问题。
内容的提问来源于stack exchange,提问作者Manna
相关产品推荐
相关产品推荐

