react 生命周期 & 执行顺序
- react生命周期
img-
第一步:初始化阶段,即( constructor() ),继承了基类,才会有render() 方法,生命周期才能使用。【这也是为什么函数组件不能使用这些方法的原因!!】;super(),用来调用基类的构造方法,将父组件的props注入子组件【props只读,不可变;state可变】
-
第二步:挂载阶段,
componentWillMount()
挂载前,调用一次,这里调用this.state不会引起组件重新渲染【这里的内容可以提到constructor中,所以很少使用】;render()
根据props和state,render一个UI,不负责实际渲染,之后由React自身根据此元素去渲染出DOM。render是纯函数,不能在里面执行this.setState()
,会有改变组件状态的副作用!;componentDidMount()
:组件挂载到DOM后调用,方法会在组件已经被渲染到 DOM 中后运行,只会调用一次! -
第三步:组件更新阶段,【⚠️setState引起的state更新、父组件重新render引起的props更新,更新后的state、props无论是否有变化都会引起子组件重新更新render!!这里可以用shouldComponentUpdate()来优化】;
-
componentWillReceiveProps(nextProps)
此方法只调用于props引起的组件更新过程中,参数nextProps是父组件传给当前组件的新props,在此方法中来查明重传的props是否改;将props转化成自己的state【在这里调用this.setState()
将不会引起第二次渲染!】因为此方法会判断props是否变化了,若变化了this.setState()
将引起state变化,进而引起render变化,此时就没必要在做第二次因重传props引起的render了; -
shouldComponentUpdate(nextProps, nextState)
返回true继续执行,false阻止组件更新,减少不必要的渲染,优化性能;就算上一个方法执行了this.state更新了state,但在render之前此方法中的this.state依然指向更新之前的!!!,否则就永远是true了; -
componentWillUpdate(nextProps, nextState)
在render之前执行,进行更新前的操作,比较少用; -
render()
重新调用 -
componentDidUpdate(prevProps, prevState)
此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState指组件更新前的props和state;
-
-
第四步:卸载阶段:
componentWillUnmount()
在组件被卸载前调用,可以执行一些清理工作,如清除定时器,清除挂载后手动创建的DOM,避免内存泄漏。
原来的生命周期在16 Fiber之后就不适合了,因为如果要开启async rendering,在render之前的所有函数都可能被执行多次!!!
-
16之前,在render前执行的生命周期:
comonentWillMount()、componentWillReceiveProps()、shouldComponentUpdate()、componentWillUpdate()
,如果在以上的方法中做ajax请求,那ajax会被无谓调用多次;如果在componentWillMount
中发起ajax,不管多快也赶不上首次render,而且此方法在服务端渲染也会被调用到,这样的io操作放在componentDidMount
里更合适。 -
所以,除了
shouldComponentUpdate()
其他render前的函数都被getDerivedStateFromProps
替代;即用一个静态函数来取代deprecate的几个生命周期函数,强制在render前只做无副作用的操作,而且能做的操作局限在根据props和state决定更新的state。 -
static getDerivedStateFromProps(props, state)
在组件创建时和更新时的render方法之前调用,应该返回一个对象来更新状态,或者返回null来不更新内容。 -
getSnapshotBeforeUpdate(prevProps, prevState)
被调用于render之后,可以读取但无法使用DOM的时候;使组件在可能更改之前从DOM捕获一些信息(如滚动位置)。此生命周期返回的任何值,都将作为参数传递给componentDidUpdate()
-
componentDidUpdate(prevProps, prevState, snapshot)
,snapshot为上一个传过来的值
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
//我们是否要添加新的 items 到列表?
// 捕捉滚动位置,以便我们可以稍后调整滚动.
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;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
- react生命周期执行顺序
-
只执行一次: constructor、componentWillMount、componentDidMount
-
执行多次:render 、子组件的componentWillReceiveProps、componentWillUpdate、componentDidUpdate
-
有条件的执行:componentWillUnmount(页面离开,组件销毁时)
-
不执行的:根组件(ReactDOM.render在DOM上的组件)的componentWillReceiveProps(因为压根没有父组件给传递props)
假设:APP有parent组件,parent有child组件
- 不涉及
setState
更新的话:
App: constructor --> componentWillMount --> render -->
parent: constructor --> componentWillMount --> render -->
child: constructor --> componentWillMount --> render -->
componentDidMount (child) --> componentDidMount (parent) --> componentDidMount (App)
- APP的
setState
事件
App: componentWillUpdate --> render -->
parent: componentWillReceiveProps --> componentWillUpdate --> render -->
child: componentWillReceiveProps --> componentWillUpdate --> render -->
componentDidUpdate (child) --> componentDidUpdate (parent) --> componentDidUpdate (App)
- 触发parent的
setState
parent: componentWillUpdate --> render -->
child: componentWillReceiveProps --> componentWillUpdate --> render -->
componentDidUpdate (child) --> componentDidUpdate (parent)
- child组件自身的
setState
child: componentWillUpdate --> render --> componentDidUpdate (child)
-
结论:完成顺序从根部到子部,完成时从子部到根部(类似事件机制);组件setState不能触发父组件的生命周期更新函数,只能触发更低一级别的生命周期更新函数。
-
子组件接收的
this.props
和componentWillReceiveProps
的nextProps
一样,是最新的,所以componentWillReceiveProps
没什么用处;但是在constructor函数中初始化了某个state,必须用componentWillReceiveProps
来更新state,不可省去,否则render中的state将得不到更新。 同时如果您想在子组件监听watch值变化做处理,也可以用到componentWillReceiveProps
-
render
中不能使用setState
,或componentWillReceiveProps进行向上分发
,会造成死循环
-