提纲如下:
1,说说react的生命周期?
2,说说react的生命周期方法?
3,各个生命周期方法的使用注意点?
一,react的生命周期
react的生命周期分为三个阶段,分别是初始化挂载阶段、更新阶段以及卸载阶段。
个人见解:不管react是使用类组件,还是使用函数组件引用useState、useEffect等hooks方法来进行开发,都有这三个周期阶段。
不同的是react在使用类组件模式时,不同react大版本所使用的生命周期方法会有所不同。react版本在迭代更新时,尝试利用新功能的API时,并尽可能的保持了较旧的API正常运行,通过警告来逐步升级,这也是导致我们很多初学者从来不关注react版本迭代发生的变化,闷头只知道使用生命周期方法。在使用react开发项目时,一定要时刻关注react版本的变化与使用,这样我们才能更好的开发项目,避免掉一些生命周期方法带给我们的bug。
二,react的生命周期方法
不同大版本的react会有不同的生命周期函数方法,我们按找历史更新发布来看,我个人整理为如下几个更新版本:
接下来,我们挨个说清楚。
1,react-15版本的生命周期方法
2,react-16.3版本的生命周期方法
我们引用官网文档提供的生命周期图谱:
3,react-16.4版本的生命周期方法
这与16.3版本的唯一区别是静态方法getDerivedStateFromProps()除了在初始化和props更新时触发的基础上,增加了state更新和forceUpdate()时也可以触发此方法。如下图所示
4,react-16.8版本
react-16.8版本主要为我们使用函数组件开发引入了hooks方法,从而可以通过函数式编程来实现类组件的相关功能(已经很接近)。
5,react-17版本
react-17版本则正式废弃了componentWillMount()、componentWillReceiveProps()和componentWillUpdate()这三个生命周期方法。
三,各个生命周期方法使用注意点
初始挂载阶段
constructor -> static getDerivedStateFromProps -> render -> componentDidMount
1,constructor。通常,在React中,构造函数仅用于以下两种情况:
- 通过this.state赋值对象来初始化内部state。
- 为事件处理函数绑定实例
在constructor中不要使用setState;
constructor(props){
super(props);
this.state = {
// 不要这样做,如果只是用一次可以这样做
color: props.color
}
}
// 这样做,会导致prop在更新时不会触发state的变化,这样会产生bug.
2,static getDerivedStateFromProps(props, state) (不常用)
- 在render方法之前调用,并且在初始挂载、更新、forceUpdate时都会调用
用法:参考
- 应用于state的值在任何时候都取决于props。
这种state依赖于props的状态为派生state.派生状态会导致代码冗余,并使组件难以维护。替代方案如下:
-
如果需要执行副作用,以响应props中的更改,请改用componentDidUpdate。
-
如果只想在prop更改时重新计算某些数据,请使用memoization。
-
如果想使用props更改重置某些state,考虑使组件完全受控或者使用key使组件不受控来代替。
state = { email: this.props.defaultEmail, prevPropsUserID: this.props.userId } static getDerivedStateFromProps(props, state){ if(props.userId !== state.prevPropsUserID){ // 更新state return { prevPropsUserID: props.userID, email: props.defaultEmail }; } return null; // 必须,为不更新state }
3,componentDidMount()
- DOM加载完成之后调用,依赖于DOM节点的初始化应该放在这里。
- 添加监听订阅适合放在这里。
- 这里可以直接调用setState,会导致render两次调用,触发额外渲染。如果不依赖DOM节点的大小和位置(比如modals和tooltips),避免使用setState。
更新阶段
getDerivedStateFromProps ->
shouldComponentUpdate ->
getSnapshotBeforeUpdate(prevProps, prevState) ->
componentDidUpdate(prevProps, prevStae, snapshot)
1,getDerivedStateFromProps (不能同时存在:componentWillReceiveProps(props,state))
功能同上
2,shouldComponentUpdate(nextProps,nextState)
不调用的情况:首次渲染和使用forceUpdate()时不会调动该方法
不建议在该方法中进行深层比较或使用JSON.stringify().
不能在该方法中使用setState,会触发循环渲染导致异常。
此方法仅作为性能优化的方式而存在。不要企图靠此方法来跳过渲染,这样会导致bug。考虑使用内置的PureComponent组件,而不是手动编写。
3,getSnapshotBeforeUpdate(prevProps, prevState) (不常用)
在更新渲染输出(提交到DOM节点)之前调用。它使得组件能在发生更改之前从DOM中捕获一些信息。此生命周期的任何返回值将作为参数传递给componentDidUpate()。
如需要以特殊方式处理滚动位置的聊天线程等。
class ScrollList extends React.Component{
constructor(props){
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState){
// 我们是否在list中添加新的item
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return this.scrollHeight - list.scrollTop;
};
return null;
},
componentDidUpdate(prevProps, prevState, snapshot){
if (snapshot != null) { // 判断snapshot不能为null或者undefined
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render(){
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
)
}
}
4,componentDidUpdate(prevProps, prevState, snapshot)
首此渲染不会执行。
组件更新后可以在此处对DOM进行操作。如果你对更新前后的props进行了比较,也可以选在在此处进行网络请求。
componentDidUpdate(prevProps, prevState){
// 典型用法(不要忘记比较props)
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
也可以在componentDidUpdate()中直接调用setState(),但必须被包裹在一个条件语句里。否则会导致死循环。
- 如果组件实现getSnapshotBeforeUpdate生命周期,则它的返回值将作为componentDidUpdate()的第三个参数'snapshot'参数传递。否则此参数为null。
- shouldComponentUpdate()返回值为false,则不会调用componentDidUpdate()。
卸载阶段
componentWillUnmount()
componentWillUnmount()中不应该使用setState。
此方法中执行必要的清理操作,例如,清除timer,取消网络请求或清除在componentDidMount()中创建的订阅等。