自我成长——React生命周期函数
在React的类组件中,官方为其提供了生命周期的概念组件的生命周期大抵分为三个阶段:挂载(mount)、更新(update)、卸载(unmount)。
挂载阶段:在组件初始化中执行且只执行一次
更新阶段:当props和state改变时触发零次或多次
卸载阶段:在组件销毁时执行且只执行一次
挂载阶段中涉及到的生命周期
1、constructor
通过给this.state赋值对象来初始化内部state
将事件处理程序方法绑定到实例。
constructor(props) {
super(props);
this.state = { name:sans };
this.initData = this.initData.bind(this);
}
ps:不要把props直接赋值给state,这样操作state不会随着props更新而变化。
在constructor中不要调用setState方法,直接给this.state赋值
2、static getDerivedStateFromProps(props, state)
getDerivedStateFromProps是一个静态方法,在挂载和更新阶段时调用,可以返回一个对象来更新状态或者返回null不更新。
可以通过此生命周期函数比较新的props和之前的state中的数据,查看是否变化,再去更新state。也可以不判断直接更新state值。
static getDetivedStateFromProps(props,state){
const { name } = props;
// 判断新的props和之前的state是否相同,如果改变就相应更新state
if( name != state.name) {
return {
name
};
}
//否则不处理
return null;
}
3、render
render是类组件中唯一必须实现的方法,在此方法中会检查props和state的变化并渲染react组件。
render(){
return(
<div>
<h1>Hello World</h1>
</div>
)
}
4、componentDidMount
此方法在组件挂载完成后调用,适用于一些异步数据请求、设置定时器、dom节点初始化的操作,只会调用一次。
ps:1、如果在此方法中设置了setInterval、setTimeout记得在componentWillUnmount里清除掉!
2、在此方法可以直接调用setState,官网解释如下:
你可以在 componentDidMount() 里直接调用 setState() 。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在 constructor() 中初始化 state。如果你的渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理
componentDidMount(){
//设置定时器
this.timer = setTimeout(() => {
console.log('component did mount')
}, 300);
//获取异步数据
this.initData();
}
更新阶段涉及到的生命周期
1、getDerivedStateFromProps
2、shouldComponentUpdate(nextProps, nextState)
在react很多场景下会造成react不必要的重新渲染,比如setState但是state的值其实没有变化,组件还是会rerender,而此时就会导致不必要的性能损耗。在这种场景下,就可以通过shouldComponentUpdate生命周期去优化代码。
该方法主要是将当前的props与nextProps比较,当前的state和nextState比较,如果返回false则可以跳过更新,但是该方法并不会阻止子组件在state中渲染,所以如果想在shouldComponentUpdate去阻止render,可能会产出bug。
shouldComponentUpdate(nextProps,nextState){
//如果值没有变化,则返回false,跳过更新
if(nextProps.name == this.props.name){
//当返回false时,后续不会调用componentWIllUpdate、render、componentDidUpdate这几个函数
//但是仍可能重新渲染
return false
}
return true
}
ps:在该生命周期中进行的是浅比较,并且不建议在其中进行深层比较或者使用JSON.stringfy(),会非常影响效率且损害性能。
3、render
当组件的props、state变更时,或者强制更新时都会触发render方法,并且render函数是一个纯函数,在class组件中必须实现。
4、getSnapShotBeforeUpdate(prevProps, prevState)
这个生命周期也是react16.4之后新出的,会在最近的一次渲染之前调用,当我们想要获取render之前的dom状态时就可以使用该方法,并且该生命周期的任何返回值将变成参数传给componentDidUpdate。
看看官网的🌰:
getSnapshotBeforeUpdate(prevProps, prevState) {
// 如果list添加了新的item,就在最新一次render前获取当前list的高度
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
// 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
//(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
5、componentDidUpdate(prevProps,prevState,snapshot)
该方法会在组件更新结束后立即执行,组件首次渲染后不会执行此方法。
componentDidUpdate(prevState){
// 如果更新后state发生了变化,就去获取data
if(this.state.name !== prevState.name) {
this.getData();
}
}
ps:如果要在该方法中执行setState操作,必须先给一层条件判断,否则会导致死循环,还会导致额外的重新渲染,影响组件性能。
卸载阶段涉及到的生命周期
1、componetWillUnmount
在组件卸载之前,会调用该生命周期。一般我们都会在该方法里去做一些清除一些事件的监听、定时器的清理或取消网络请求等操作。
componentWillUnmount(){
// 清除定时器
clearTimeout(this.timer);
}
ps:在这里调用setState没啥用,组件马上就销毁了不会再重新渲染。
React新旧生命周期的对比
综上所述,在新生命周期中,有三个生命周期方法即将过时,要避免使用并且使用前要加上UNSAFE_前缀
并且添加了两个新的生命周期函数
总结:
在使用生命周期时,还要首先把握每一个方法的使用场景,避免造成一些不必要的性能损耗和死循环(如在componentDidUpdate中直接setState),只有了解了生命周期的顺序和各方法作用,才能写出更好地代码,冲冲冲。