每一个 React 组件的实例都拥有着一套完善的生命周期,主要分为挂载、更新和卸载三个大阶段,每一个阶段都拥有着更为细化的生命周期。
挂载阶段相关的生命周期 API:
static getDefaultProps
initialize state
constructor(props)
static getDerivedStateFromProps(nextProps, prevState)
(V 16.3.0)componentWillMount()
/UNSAFE_componentWillMount()
render()
componentDidMount()
更新阶段相关的生命周期 API:
componentWillReceiveProps(nextProps)
/UNSAFE_componentWillreceiveProps(nextProps)
static getDerivedStateFromProps(nextProps, prevState)
(V 16.3.0)shouldComponentUpdate(nextProps, nextState)
componentWillUpdate(nextProps, nextState)
/UNSAFE_componentWillUpdate(nextProps, nextState)
render()
getSnapshotBeforeUpdate(prevProps, prevState)
(V 16.3.0)componentDidUpdate(prevProps, prevState, snapshot)
卸载阶段相关的生命周期 API:
componentWillUnmount()
定义默认属性
class MyComponent extends React.Component {
static getDefaultProps = {}
// ...
}
初始化 state。
当前版本的官方文档(V 16.3.0)未说明该阶段。在 React V 16.0.0 之前,React.createClass 仍未废弃的情况下,还包含着这个阶段相关的 api。当前的官方文档中的示例,初始化
state
都是在constructor
中完成。但是文档中说到_(The constructor is the right place to initialize state. _To do so, just assign an object to _this.state
),_猜测实际上仍存在该阶段
class MyComponent extends React.Component {
// ES7 语法实现初始化 state
state = {}
// ...
}
构造器函数,在此阶段进行初始化 state 和事件处理器(event handler)的上下文绑定
React.Component
的子类的构造器函数里,一定得调用super(props)
,否则this.props
将会是undefined
- 不要在这个阶段调用this.setState(),倘若要改变 state,只需要 state 对象进行 assign 操作即可
- 如果,不需要初始化 state 也不打算给 class 实例绑定,事件处理器,就没有必要实现
conostructor
- 在此阶段改变 state 时,不处理受到 props 影响的值,而是在
componentWillMount
或getDerivedStateFrom
内处理
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.btnClickHandler = this.btnClickHandler.bind(this);
}
btnClickHandler (e) {}
// ...
}
React v16.3.0新增 API。getDerivedStateFromProps()
在组件初始化和接收新的 props 时调用。将会返回一个 object 去更新 state,或是返回一个null
表示新的 props 不会导致 state 更新。
- 无论 props 是否改变,该函数都会被调用
- 通常情况下,
this.setState()
不会触发该函数- 一旦使用了这个新的生命周期函数,不安全的生命周期函数将不会被调用
class MyComponent extends React.Component {
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProp.value !== prevState.selectedVal) {
return {
selectedVal: nextProps.value,
}
}
return null;
}
// ...
}
componentWillMount()
的别名。在即将挂载的时候调用。由于该阶段在render
之前,因此,在该阶段异步的调用setState()
不会触发额外的渲染。在挂载之前,最后一次允许改变 state 的阶段。
componentWillMount
这个名称计划在 React V17.0.0 之后废弃掉。react 提供了 rename-unsafe-lifecycles codemod 这个工具去自动更新- 该生命周期函数会受到
static getDerivedStateFromProps()
和getSnapshotBeforeUpdate()
的影响而无法使用
class MyComponent extends React.Component {
UNSAFE_componentWillMount() {
if (this.props.mod === 'mod1') {
this.setState({
mod1State
});
}
}
// ...
}
componentDidMount()
在挂载完成后调用。在该阶段可进行加载远程数据,订阅事件,设置定时器等操作。
该阶段调用 setState() 会触发额外的渲染,处理不当可能会导致死循环
class MyComponent extends React.Component {
_loadData() { // ... }
componentDidMount() {
this._loadData();
this.autoLoadDataIntevalKey = setInterval(this._loadData, 30 * 1000);
this.subscriptKey = subscriptNoticeMsg((msg) => {
alert(msg);
});
}
// ...
}
componentWillReceiveProps()
的新名称,在一个已经挂载的组件接收到新 props 时被调用。如果需要在此阶段根据 props 更新 state,最后先比较 this.props
和nextProps
后再调用setState()
- 如果是父组件导致重新渲染,即使 props 没变更也会被调用
componentWillReceiveProps
计划在 React V17.0.0 之后废弃掉- 该生命周期函数会受到
static getDerivedStateFromProps()
和getSnapshotBeforeUpdate()
的影响而无法使用
class MyComponent extends React.Component {
componentWillReceiveProps(nextProps) {
if (nextProps.value === this.props.value) {
this.setState({
selectedVal: nextProps.value,
});
}
}
// ...
}
shouldComponentUpdate()
在一个已经挂载的组件接收到新的 props 或 state 时被调用,其返回一个布尔值告诉 React 是否应该进行更新。默认情况下,每一次的状态变更都会进行重新渲染。
当shouldComponentUpdate()
的返回false
时,UNSAFE_componentWillUpdate()
,render()
和componentDidUpdate()
都不会被调用。
对于React.PureComponent
的子类,shouldComponentUpdate()
对 props 和 state 进行了一次浅比较。
如果,
React.PureComponent
的子类重写了shouldComponentUpdate()
React 在开发环境下会有以下警告:Warning: XXXX has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
for (let key in nextProps) {
if (nextProps[key] === this.props[key]) {
return true;
}
}
for (let key in nextState) {
if (nextState[key] === this.state[key]) {
return true;
}
}
}
// ...
}
componentWillUpdate()
的新名称,在一个已经挂载的组件接收到新的 props 或 state,并且shouldComponentUpdate()
返回true
时被调用。这是在更新之前最后一次准备的机会。
- 不要在这个阶段调用
this.setState()
,dispatch Redux 的 action 也一样,否则,可能会导致 React 组件在UNSAFE_componentWillUpdate()
返回前就进行了一次更新。componentWillUpdate
计划在 React V17.0.0 之后废弃掉- 该生命周期函数会受到
static getDerivedStateFromProps()
和getSnapshotBeforeUpdate()
的影响而无法使用
React V16.3.0 新增的生命周期。在当前已挂载的组件调用 render() 之后调用。该函数的返回值将作为componentDidMount()
的第三个参数传递下去。
一旦使用了这个新的生命周期函数,不安全的生命周期函数将不会被调用
class ScrollingList extends React.Component {
listRef = React.createRef();
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the current height of the list so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
return this.listRef.current.scrollHeight;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
if (snapshot !== null) {
this.listRef.current.scrollTop +=
this.listRef.current.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
componentDidUpdate()
在更新结束后立刻被调用,可以在这个阶段进行 DOM 的操作、网络请求等。
class MyComponent extends React.Component {
_loadData() {}
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.province !== this.props.province) {
this._loadData();
this.amap.setProvince(this.props.province)
}
}
// ...
}
当一个组件即将被卸载和销毁时会立刻调用componentWillUnmount()
函数,在这个阶段可以进行取消网络、清空订阅等操作。
class MyComponent extends React.Component {
componentWillUnmount() {
clearInterval(this.intervalKey);
unsubscript(this.subscriptKey);
}
// ...
}