Office UI组件中componentWillReceiveProps未触发问题排查
首先,咱们来拆解你遇到的几个核心问题:
1. 重复定义componentWillReceiveProps导致的冲突
你在constructor里手动赋值了this.componentWillReceiveProps,又用@autobind装饰器定义了另一个同名方法——这会导致后者覆盖前者,而且在constructor里绑定生命周期方法本身就是错误的写法,会干扰React的生命周期执行逻辑。
2. componentWillReceiveProps已被废弃(React 16.3+)
这个生命周期方法在新版本React中已经被标记为过时,推荐使用static getDerivedStateFromProps或者componentDidUpdate来处理props更新的场景。
3. 父组件的_sumEngagement可能未触发重渲染
如果父组件里的_sumEngagement只是一个普通变量(而非存在state中),当它的值变化时,父组件不会自动触发render,子组件自然也不会收到新的props,这就解释了为什么你的生命周期方法完全没触发。
具体修复步骤
第一步:修复父组件的_sumEngagement状态管理
确保_sumEngagement是父组件state的一部分,当它的值更新时,调用setState触发父组件重渲染:
// 父组件示例 class ParentComponent extends React.Component { state = { _sumEngagement: '' // 初始值 }; // 当计算出新的_sumEngagement时,更新state updateSumEngagement = (newSum: string) => { this.setState({ _sumEngagement: newSum }); }; render() { return ( <> <NumberField label="Quantité Engagement" value={this._item.BudgetEngagementDepenseQte} returnValue={this.OnQteEngagementChange} disabled={this.props.readOnly} /> <NumberField label="Montant total engagement" value={this.state._sumEngagement} // 这里用state中的值 returnValue={() => {}} disabled={true} /> </> ); } }
第二步:重构NumberField组件的props同步逻辑
去掉重复的componentWillReceiveProps定义,改用static getDerivedStateFromProps来同步props到state:
import * as React from 'react' import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { autobind } from '@uifabric/utilities'; import {CustomFormat} from '../../Tools/CustomFormat' export class NumberField extends React.Component< { label?: string, disabled?: boolean, value?: string, returnValue(value: string): void }, { label: string, disabled: boolean, value: string, } > { constructor(props) { super(props) this.state = { label: this.props.label || '', disabled: this.props.disabled || false, value: this.props.value || '' }; // 移除这里的componentWillReceiveProps赋值! } // 用这个静态方法同步props到state static getDerivedStateFromProps(nextProps, prevState) { // 当props的value变化时,更新state if (nextProps.value !== prevState.value) { console.log(`nextprops value: ${nextProps.value}`); return { value: nextProps.value || '' }; } // 不需要更新时返回null return null; } render() { return( <TextField label={this.state.label} disabled={this.state.disabled} value={this.state.value} onChanged={this.onChanged} /> ) } @autobind onChanged(value: string) { let _value = CustomFormat.ParseNumber(value); console.log(_value); this.props.returnValue(_value.toString()) this.setState({ value: CustomFormat.formatNumber(_value) }); } }
可选优化:避免不必要的state维护
如果你的组件只是对props的value做格式化展示,其实可以考虑直接在render中使用props.value,只把用户输入的临时值存在state里,这样能减少状态同步的复杂度:
// 简化版思路 render() { // 展示时用格式化后的props.value,用户输入时用state的临时值 const displayValue = this.state.userInput || CustomFormat.formatNumber(this.props.value); return( <TextField label={this.props.label || ''} disabled={this.props.disabled || false} value={displayValue} onChanged={this.onChanged} /> ) }
这样修改后,当父组件的_sumEngagement通过setState更新时,会触发父组件重渲染,NumberField会收到新的value props,getDerivedStateFromProps会检测到变化并更新组件state,最终TextField的值就会同步更新了。
内容的提问来源于stack exchange,提问作者cyril Souied

