使用背景
我很不喜欢在代码里写componentWillReceiveProps,因为这样会导致子组件太过灵活,你并不知道何时就改掉了子组件的state。但是,当子组件需要根据父组件更新的props中的某个属性来更新自身的state来做到重新渲染子组件时,我又只能鬼使神差的写下这么一段自己都不喜欢的代码:
class Demo extends Component {
state = {
value: this.props.value
};
componentWillReceiveProps(props){
if(props.value !== this.state.value){
this.setState({value:props.value})
}
if(this.props.value){
// 做一些需要this.props的事
}
}
// ...
}
这种代码应该在实际React应用中很常见,在新版本的React中,componentWillReceiveProps被标记为unsafe,同时,官方出了一个新的生命周期getDerivedStateFromProps,官宣称配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法
说明书
componentWillReceiveProps可以使用this.props,不仅可以做setState操作,还可以和this.props进行比对去做一些日常的方法的操作。
componentWillReceiveProps(props){
if(props.value !== this.state.value){
this.setState({value:props.value})
}
if(this.props.value){
// 做一些需要this.props的事
}
}
getDerivedStateFromProps禁止访问this.props,强制指定props只能和state进行比对,可以说是为setState而创建的。
static getDerivedStateFromProps(props,state){
if(props.value !== state.value){
return {
value:props.value
}
}
return null
}
而那些需要this.props做的事则通过componentDidUpdate来做,也就解释了上面所说的getDerivedStateFromProps + componentDidUpdate = componentWillReceiveProps
componentDidUpdate(){
if(this.props.value){
// 做一些需要this.props的事
}
}
可以说新的生命周期使得什么时候做什么事变得更清晰了,通过强制的不暴露this.props来将componentWillReceiveProps拆分成了拥有各自职责的getDerivedStateFromProps和componentDidUpdate
这俩只有这一种区别吗?
食用测试
我们分别在父组件的render和子组件的componentWillReceiveProps和getDerivedStateFromProps打印日志
父组件
class Index extends React.Component {
// ...
render(){
console.log('父组件更新')
<Demo value={this.state.value}>
}
}
子组件使用componentWillReceiveProps
class Demo extends Component {
state = {
value: this.props.value
};
componentWillReceiveProps(props){
console.log('componentWillReceiveProps')
if(props.value !== this.state.value){
this.setState({value:props.value})
}
}
render(){
console.log('子组件更新')
// ...
}
}
子组件初始化打印结果:
父组件更新
子组件更新
子组件更新state.value的打印结果:
子组件更新
父组件更新
componentWillReceiveProps
子组件更新
2.子组件使用getDerivedStateFromProps
class Demo extends Component {
state = {
value: this.props.value
};
static getDerivedStateFromProps(props,state){
console.log('getDerivedStateFromProps')
if(props.value !== state.value){
return {
value:props.value
}
}
return null
}
render(){
console.log('子组件更新')
// ...
}
}
子组件初始化打印结果:
父组件更新
getDerivedStateFromProps
子组件更新
子组件更新state.value的打印结果:
getDerivedStateFromProps
子组件更新
父组件更新
getDerivedStateFromProps
子组件更新
这让我们更清晰的认识了getDerivedStateFromProps函数,他不仅在props变化的时候触发,而是在每次render(state和props更新)时就会触发
不只是子组件本身的render,当父组件一个无关本组件的state发生了变化,也会触发getDerivedStateFromProps
大多数情况下替代componentWillReceiveProps的方法
当我打算将getDerivedStateFromProps取代我的componentWillReceiveProps时,看到了一篇来自React的官方blog 你可能不需要使用派生 state,虽然是去年的文章了,但是看到依然觉得有种想要双击666的感觉。
熟悉React的童鞋都知道,key值是否相同是React决定是否重新创建还是更新组件的原因。
同样的,我们可以采用这个原理做我们刚才的范例。
在我们的父组件设置好key值:
<Demo key={item.value} callback={changeValue}>
去掉子组件里的getDerivedStateFromProps/componentWillReceiveProps
每次这个Demo组件的state.value更改,触发了props的更改,我们之前在getDerivedStateFromProps所做的就是在props更改时同时改掉子组件的state。
现在我们通过赋值key的方法,使每次key变动时,重新创建Demo组件,这听起来很慢,但是这点的性能是可以忽略的。如果Demo组件树上有很重的逻辑时,创建反而比更新快,因为省略了子组件的diff
大部分情况下,这是处理重置 state 的最好的办法。