目前React的版本为16.x版本,此文章主要针对的16.3后版本的生命周期的说明
1. 为什么我们要学习组件的生命周期钩子函数
2. React生命周期阶段
我们将React生命周期分为四个阶段,分别如下:
- 挂载阶段
- 更新阶段
- 卸载阶段
- 错误处理阶段【 React16.3版本之后新增 】
3. React生命周期注意点
React16废弃的三个生命周期函数
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
目前在16版本中componentWillMount,componentWillReceiveProps,componentWillUpdate并未完全删除这三个生命周期函数,而且新增了UNSAFEcomponentWillMount,UNSAFE_componentWillReceiveProps,UNSAFE_componentWillUpdate三个函数,官方计划在17版本完全删除这三个函数,只保留UNSAVE前缀的三个函数,目的是为了向下兼容,但是对于开发者而言应该尽量避免使用他们,而是使用新增的生命周期函数替代它们
取而代之的是两个新的生命周期函数
- static getDerivedStateFromProps
- getSnapshotBeforeUpdate
3. React生命周期图示
4. React生命周期各阶段钩子函数解析
初始化阶段就是将我们的组件插入到DOM中,只会发生一次
- 这个阶段的生命周期函数调用如下:
- constructor
- getDerivedStateFromProps
- componentWillMount/UNSAVE_componentWillMount
- render
- componentDidMount
constructor 构造器函数
- 触发时间: React组件的构造函数在挂载之前被调用
- 作用:
- 调用super(props),用来将父组件传来的props绑定到这个类中,使用this.props将会得到
- 做一些初始化的操作,比如: state的初始化、非箭头函数的实例属性的this绑定
- 说明
- 禁止在constructor中调用setState,可以直接给state赋初始值
代码演示
import React,{ Component } from 'react' class Hello extends Component{ constructor ( props ) { super( props ) // 通过调用super将父组件绑定在当前组件身上的属性赋值给this.props this.state = { // 做state的初始化 count: 0 } this.change = this.change.bind( this ) // 进行初始化this的指向修改 } change () { // 如果不进行bind的绑定,那么该实例方法的this值为undefined console.log('this的值为:',this ) } render () { return ( <div> </div> ) } }
getDerivedStateFromProps
- 调用时间: 该函数会在挂载时,接收到新的props,调用了setState和forceUpdate时被调用
- 作用:
- 一个静态方法,所以不能在这个函数里面使用this,这个函数有两个参数props和state,分别指接收到的新参数和当前的state对象,这个函数会返回一个对象用来更新当前的state对象,如果不需要更新可以返回null
- 代码演示
class ExampleComponent extends React.Component { state = { isScrollingDown: false, lastRow: null } static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.currentRow !== prevState.lastRow) { return { isScrollingDown: nextProps.currentRow > prevState.lastRow, lastRow: nextProps.currentRow } } return null } }
componentWillMount OR UNSAFE_componentWillMount
在16版本这两个方法并存,但是在17版本中componentWillMount被删除,只保留UNSAFE_componentWillMount,目的是为了做向下兼容,对于新的应用,用getDerivedStateFromProps代替它们由于componentWillMount/ UNSAFE_componentWillMount是在render之前调用,所以就算在这个方法中调用setState也不会触发重新渲染(re-render)
render 函数
React中最核心的方法,一个组件中必须要有这个方法
- 触发时间:初始化阶段和更新阶段都会触发
- 作用:
- 返回需要渲染的vdom
- 进行this.props && this.state 的计算
- 说明
- render函数式必须要有返回值的,返回值的数据类型可以为:
- React元素
- React组件
- Fragment( 片段 )
- Portals【 插槽 】
- 字符串或是数组,被渲染成text节点
- Boolean和null,不会渲染任何东西
- render函数式必须要有返回值的,返回值的数据类型可以为:
- 代码演示
import React,{ Component } from 'react' class Hello extends Component{ constructor ( props ) { super( props ) this.state = { msg: 'Hello 西阁' } } render () { const { msg } = this.state return ( <div> { msg } </div> ) } }
componentDidMount
- 调用时间: 组件挂载结束之后调用
- 作用
- 可以做数据请求,并将请求之后的结果赋值给组件的state
- 可以进行DOM操作
- 可以进行第三方库的实例化
代码演示
import React from 'react' class Hello extends React.Component{ constructor ( props ) { super( props ) this.state = { data: null } } componentDidMount () { // 进行数据请求,然后赋值给state fetch('http://jsonplaceholder.typicode.com/albums') .then( data => data.json() ) .then( res => { this.setState({ data: res }) }) .catch( err => console.log( err )) } render () { return ( <div></div> ) } }
这个阶段的生命周期函数调用如下:更新阶段,当组件的props改变了,或组件内部调用了setState或者forceUpdate发生,会发生多次
- componentWillReceiveProps/UNSAFE_componentWillReceiveProps
- getDerivedStateFromProps
- shouldComponentUpdate
- componentWillUpdate/UNSAFE_componentWillUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
componentWillReceiveProps/UNSAFE_componentWillReceiveProps
在16版本这两个方法并存,但是在17版本中componentWillReceiveProps被删除,UNSAFE_componentWillReceiveProps,目的是为了做向下兼容,对于新的应用,用getDerivedStateFromProps代替它们
- 触发条件: 组件的props发生改变时触发
- 作用
- 用于接收新的属性,然后做一些props改变的监听
- 代码演示
import React from 'react' class Hello extends Component{ componentWillReceiveProps( nextProps ) { console.log('nextProps',nextProps) //用于接收变化后的新的属性 } render () { return ( <div></div> ) } }
getDerivedStateFromProps
初始化阶段已经解释过,这里就不在多解释
shouldComponentUpdate( nextProps,nextState )
- 有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true
- 因为默认是返回true,也就是只要接收到新的属性和调用了setState都会触发重新的渲染,这会带来一定的性能问题,所以我们需要将this.props与nextProps以及this.state与nextState进行比较来决定是否返回false,来减少重新渲染
- 注意
- 但是官方提倡我们使用PureComponent来减少重新渲染的次数而不是手工编写shouldComponentUpdate代码
- 代码演示
import React from 'react' class Hello extends React.Component{ shouldComponentUpdate ( nextProps,nextState ) { return this.props.xxx == nextProps.xxx } render () { return ( <div></div> ) } }
componentWillUpdate/UNSAFE_componentWillUpdate
- 在16版本这两个方法并存,但是在17版本中componentWillUpdate被删除,UNSAFE_componentWillUpdate,目的是为了做向下兼容 在这个方法里,你不能调用setState,因为能走到这个方法,说明shouldComponentUpdate返回true,此时下一个state状态已经被确定,马上就要执行render重新渲染了,否则会导致整个生命周期混乱,在这里也不能请求一些网络数据,因为在异步渲染中,可能会导致网络请求多次,引起一些性能问题,
- 如果你在这个方法里保存了滚动位置,也是不准确的,还是因为异步渲染的问题,如果你非要获取滚动位置的话,请在getSnapshotBeforeUpdate调用
render
更新阶段也会再次出发
getSnapshotBeforeUpdate(prevProps, prevState)
- 调用时间: render结束之后,componentDidUpdate调用之前
- 作用
- 这个函数有一个返回值,会作为第三个参数传递给componentDidUpdate
- 注意
- 这个方法一定要和componentDidUpdate一起使用,否则控制台也会有警告
- 代码演示
import React from 'react' class Hello extends React.Component{ render () { return ( <div></div> ) } getSnapshotBeforeUpdte (prevProps, prevState) { return document.documentElement.scrollTop } componentDidUpdate(prevProps, prevState, snapshot) { console.log("西阁: Hello -> componentDidUpdate -> snapshot", snapshot) } }
- componentDidUpdate(prevProps, prevState, snapshot)
- 调用时间: getSnapshotBeforeUpdter调用之后
- 作用
- 这个函数可以通过snapshot参数来接收getSnapshotVeforeUpdater的返回值
- 进行DOM操作
- 发起服务器请求
- 进行第三方库实例化
- 注意
- 这个方法还可以setState,但是注意一定要用if语句控制,否则会导致无限循环
- 代码演示
import React from 'react' class Hello extends React.Component{ render () { return ( <div></div> ) } getSnapshotBeforeUpdte (prevProps, prevState) { return document.documentElement.scrollTop } componentDidUpdate(prevProps, prevState, snapshot) { console.log("西阁: Hello -> componentDidUpdate -> snapshot", snapshot) } }
- 触发时间: 当我们的组件被卸载或者销毁了就会调用,
- 作用:
- 我们可以在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作
- 代码演示
import React from 'react' class Hello extends React.Component{ render () { return ( <div></div> ) } componentWillUnmount () { clearInterval( this.timer ) window.onscroll = null } }
componentDidCatch(error, info)
- 调用: 此生命周期在后代组件抛出错误后被调用。 它接收两个参数:
- error —— 抛出的错误。
- info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。
- 作用:
- componentDidCatch() 会在“提交”阶段被调用,因此允许执行副作用。 它应该用于记录错误之类的情况:
- 错误边界是React组件,可以在其子组件树中的任何位置捕获JavaScript错误,记录这些错误并显示回退UI,而不是崩溃的组件树。错误边界在渲染期间,生命周期方法以及整个树下的构造函数中捕获错误
- 代码演示
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // 更新 state 使下一次渲染可以显示降级 UI return { hasError: true }; } componentDidCatch(error, info) { // "组件堆栈" 例子: // in ComponentThatThrows (created by App) // in ErrorBoundary (created by App) // in div (created by App) // in App logComponentStackToMyService(info.componentStack); } render() { if (this.state.hasError) { // 你可以渲染任何自定义的降级 UI return <h1>Something went wrong.</h1>; } return this.props.children; } }- 调用: 此生命周期在后代组件抛出错误后被调用。 它接收两个参数: