一直使用componentWillReceiveProps来进行当props发生变化时更新state,重新渲染。但是使用react16.3及以上版本时,提示componentWillReceiveProps将在17版本时被废弃。可以使用getDerivedStateFromProps来进行替代。于是研究了一下如何用getDerivedStateFromProps替代componentWillReceiveProps。
1 含义
getDerivedStateFromProps生命周期的意思就是从props中获取state,其功能实际上就是将传入的props映射到state上面。
这个函数会在每次re-rendering之前被调用,意味着即使你的props没有任何变化,而是父state发生了变化,导致子组件发生了re-render,这个生命周期函数依然会被调用。
2 使用对比
基本使用
使用componentWillReceiveProps时:
componentWillReceiveProps(nextProps){
let t_aTC_param = nextProps.articleList;
this.setState({ s_aI4_tag_id: t_aTC_param.tag_id, s_str_tag_name: t_aTC_param.tag_name }) //更新state
}
而getDerivedStateFromProps是一个静态函数,也就是这个函数不能通过this访问到class的属性,也并不推荐直接访问属性。而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state。
需要注意的是,如果props传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾。
static getDerivedStateFromProps(nextProps, prevState) {
const { tag_id,tag_name } = nextProps.articleList;
if(tag_id !== prevState.s_aI4_tag_id){ //传入的props与state不同
return {s_aI4_tag_id:tag_id,s_str_tag_name:tag_name} //将传入的props映射到state上
}
return null
}
componentWillReceiveProps的写法并不存在什么功能上的问题,将componentWillReceiveProps标记为deprecated的原因也并不是因为功能问题,而是性能问题。
当外部多个属性在很短的时间间隔之内多次变化,就会导致componentWillReceiveProps被多次调用。这个调用并不会被合并,如果这次内容都会触发异步请求,那么可能会导致多个异步请求阻塞。
而setState操作是会通过transaction进行合并的,由此导致的更新过程是批量的,而react中大部分的更新过程的触发源都是setState,所以render触发的频率并不会非常频繁
因此在使用getDerivedStateFromProps的时候,遇到了上面说的props在很短的时间内多次变化,也只会触发一次render,也就是只触发一次getDerivedStateFromProps,从而提高了性能。
异步
以前,可以在props发生改变的时候,在componentWillReceiveProps中进行异步操作,将props的改变驱动到state的改变。
componentWillReceiveProps(nextProps){
let t_aTC_param = nextProps.articleList;
this.setState({ s_aI4_tag_id: t_aTC_param.tag_id, s_str_tag_name: t_aTC_param.tag_name }) //更新state
this.query(t_aTC_param.pageIndex, t_aTC_param.tag_id); //根据新的props内容,进行异步数据查询
}
那么,如何使用getDerivedStateFromProps进行异步的处理呢?
static getDerivedStateFromProps(nextProps, prevState) {
const { tag_id,tag_name } = nextProps.articleList;
if(tag_id !== prevState.s_aI4_tag_id){
return {s_aI4_tag_id:tag_id,s_str_tag_name:tag_name}
}
return null
}
// 在componentDidUpdate中进行异步操作,驱动数据的变化
componentDidUpdate(prevProps,prevState){
if(prevState.s_aI4_tag_id !== this.state.s_aI4_tag_id){ //前一个state与新的state不符,触发异步更新
//异步刷新数据
this.query(1, this.state.s_aI4_tag_id); //异步更新数据
}
}
3 建议
根据官网建议,getDerivedStateFromProps (以及其他派生 state)是一个高级复杂的功能,应该保守使用。
设计组件时参考以下建议方案:
完全可控的组件
从组件里删除 state
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
有 key 的非可控组件
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
父组件:
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
可以使用 key 这个特殊的 React 属性。当 key 变化时, React 会创建一个新的而不是更新一个既有的组件,每次 ID 更改,都会重新创建 EmailInput ,并将其状态重置为最新的 defaultEmail 值