React生命周期详解
React
严格定义了组件的生命周期会经历如下三个过程:
- 装载过程(Mount),组件第一次在DOM树渲染的过程。
- 更新过程(Update),当组件被重新渲染的过程。
- 卸载过程(Unmount),组件重DOM树中删除的过程。
执行这3个过程的调用函数就是声明周期函数。
装载过程
该过程会依次调用如下函数:
constructor()
:ES6类的构造函数(为了初始化state
或绑定this
)getInitialState()
:ES5中初始化state
。getDefaultProps()
:ES5中初始化props
。在ES6中使用defaultProps()
方法。componentWillMount()
:在组件被挂载前调用。只执行一次。render()
:渲染组件,必须实现该方法。componentDidMount()
:在组件装载后调用。这时已经生成了真实的DOM节点。只执行一次。
更新过程
当组件的props或者state改变时就会触发组件的更新过程。
更新过程会依次执行如下函数:
componentWillReceiveProps(nextProps)
:当父组件的render()
方法执行后就会触发该方法。初始化时不调用。shouldComponentUpdate(nextProps,nextState)
:当props
改变或state
改变时调用,初始化时不掉用,返回boolean
。true
表示继续执行render
方法,fasle
表示放弃本次渲染。componentWillUpdate(nextProps,nextState)
:当shouldComponentUpdate
返回true
时调用,初始化不调用。render()
:渲染组件。componentDidUpdate(prevProps,prevState,snapshot)
:渲染完成后调用,初始化不调。prevProps
和prevState
是指组件更新前的props
和state
。
卸载过程
componentWillUnmount()
:将组件从DOM树移出,防止内存溢出。
实例演示
组件调用:
import React from 'react'
import App from './components/App.js'
import {render} from 'mirrorx'
// 启动 app,render 方法是加强版的 ReactDOM.render
render(<App/>, document.getElementById('root'))
组件定义:
import React from 'react'
import mirror, { actions, connect, render } from 'mirrorx'
// 声明 Redux state, reducer 和 action,
// 所有的 action 都会以相同名称赋值到全局的 actions 对象上
mirror.model({
name: 'app',
initialState: 0,
reducers: {
increment(state) { return state + 1 },
decrement(state) { return state - 1 }
},
effects: {
async incrementAsync() {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
actions.app.increment()
}
}
})
class App extends React.Component {
constructor(props){
super(props)
console.log("---初始化组件---")
}
componentWillMount(){
console.log("---组件挂载前---")
}
componentDidMount(){
console.log("---组件挂载后---")
}
componentWillReceiveProps(nextProps){
console.log("---父组件重新渲染---")
}
shouldComponentUpdate(nextProps,nextState){
console.log("---组件接受到重绘状态---")
if(this.props != nextProps || this.state != nextState)
return true
}
componentWillUpdate(nextProps,nextState){
console.log("---组件即将重绘---")
}
componentDidUpdate(prevProps,prevState){
console.log("---组件重绘完成---")
}
render() {
console.log("---组件渲染---")
const props = this.props
return (
<div>
<h1>{props.count}</h1>
{/* 调用 actions 上的方法来 dispatch action */}
<button onClick={() => actions.app.decrement()} style={{ margin: "5px" }}>-</button>
<button onClick={() => actions.app.increment()} style={{ margin: "5px" }}>+</button>
{/* dispatch async action */}
<button onClick={() => actions.app.incrementAsync()} style={{ margin: "5px" }}>+ Async</button>
</div>
)
}
}
export default connect((state) => {
return {
count: state.app
}
})(App)
页面效果
当页面刷新时,组件进行初始化价值,打印信息如下:
—初始化组件—
—组件挂载前—
—组件渲染—
—组件挂载后—
当点击+号时,组件进行更新操作,打印信息如下:
—父组件重新渲染—
—组件接受到重绘状态—
—组件即将重绘—
—组件渲染—
—组件重绘完成—
React 16.0版本修改
React 16.0
之后的版本生命周期有所修改。
react 16版本提出了异步渲染的概念,对之前的生命周期有较大的影响。
componentDidCatch(error,info)
:该生命周期是React 16.0
版本提出的,用于捕获render
阶段出现的错误,并不影响之前的生命周期。
static getDerivedStateFromProps(props, state)
:render()
之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容。组件创建和更新时均将调用。在React 16.3
提出,16.4版本
修复,属于新增生命周期。
getSnapshotBeforeUpdate(prevProps, prevState)
:在render
之后componentDidUpdate
之前调用,可以读取dom结构,但无法操作dom元素,其返回的任何值都将传递给componentDidUpdate
。
在React 17版本中将废弃
componentWillMount
、componentWillReceiveProps
、componentWillUpdate
这3个方法,但也提供了带UNSAFE_componentWillMount
、UNSAFE_componentWillReceiveProps
、UNSAFE_componentWillUpdate
作为兼容考虑,只是不提倡使用。