我一直在用React编程一段时间,但我从来没有遇到过这个恼人的问题,在componentWillReceiveProps
的setState()
被执行之前,我的一个组件componentDidMount
开火了。这会在我的应用程序中导致一些问题
我有一个从道具收到的变量this.props.flag
,它将被存储在组件的状态中:
componentDidMount() {
if (!_.isEmpty(this.props.flag)) {
console.log('Flag Did:', this.props.flag);
this.setState({
flag: this.props.flag
},
() => doSomething()
);
}
在我的componentWillReceiveProps
方法中,变量this.state.flag
将被替换,只要它是空的或者它与this.props.flag
的值不同(检查是通过使用lodash库进行的):
componentWillReceiveProps(nextProps) {
const { flag } = this.state;
console.log('Flag Will:', !_.isEqual(flag, nextProps.flag), flag, nextProps.flag);
if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
假设在这种情况下,支柱flag
始终具有相同的值,并且this.state.flag
被初始化为undefined
。当我检查控制台日志时,我看到以下结果:
Flag Did: true
Flag Will: true undefined true
因此,当代码进入componentWillReceiveProps
时,this.state.flag
is的值仍然是undefined
,这意味着setState
中的componentDidMount
尚未设置。
这与React生命周期不一致或者我错过了什么?我该如何避免这种行为?
ComponentWillReceiveProps()
将在每个更新生命周期中被调用,因为更改了props(父组件重新呈现)。由于Javascript是同步的,因此您可能有时会验证道具以保存应用程序崩溃。我还没有完全理解你的应用程序的上下文,但你可以做的是:
componentWillReceiveProps(nextProps) {
const { flag } = this.state;
if(!flag){
return;,
}
console.log('Flag Will:', !_.isEqual(flag, nextProps.flag), flag, nextProps.flag);
if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
如果状态未定义,您可以返回。在父级重新渲染时将再次调用它。但这可能不是用例。
无论如何你应该看看this:
但我可以想到至少有1个(可能是理论上的)情况,订单会逆转:
组件接收道具,并开始渲染。虽然组件正在渲染,但尚未完成渲染,但组件会收到新的道具。触发了componentWillReceiveProps()(但是还没有触发componentDidMount)在所有子元素和组件本身完成渲染之后,componentDidMount()将触发。所以componentDidMount()不是初始化组件变量的好地方,比如你的{foo:'bar'}。 componentWillMount()将是一个更好的生命周期事件。但是,我不鼓励在反应组件中使用组件范围的变量,并坚持设计原则:
所有组件变量都应该存在于state或props中(并且是不可变的)所有其他变量都受生命周期方法的约束(并且不会超出该范围)
正如用户JJJ所建议的那样,鉴于setState
的异步性质,if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag))
中的componentWillReceiveProps
检查是在setState
执行componentDidMount
之前执行的flag: this.props.flag
之前执行的。操作的顺序是:
componentDidMount
。flag: this.props.flag
尚未发生)。componentDidMount
,setState
的componentDidMount
仍在执行中(flag: this.props.flag
尚未发生)。componentWillReceiveProps
。if
(!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag))
中的componentWillReceiveProps
声明(this.state.flag
仍未定义)。setState
内完成componentDidMount
的执行并设置flag: this.props.flag
并执行doSomething()
。setState
内完成componentWillMount
的执行并设置flag: nextProps.flag
并执行doSomething()
。鉴于setState
的异步性质,6和7可以并行执行,因此我们不知道哪个将首先完成其执行。在这种情况下,DoSomething()
在被调用一次时可能被称为至少2次。
为了解决这些问题,我改变了我的代码:
componentWillReceiveProps(nextProps) {
if (!_.isEmpty(nextProps.flag) && !_.isEqual(this.props.flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
这样我就可以将新版本(nextProps
)与道具的旧版本(this.props
)进行比较,而无需等待flag
值存储在组件的状态中。