组件的生命周期

目前React的版本为16.x版本,此文章主要针对的16.3后版本的生命周期的说明

1. 为什么我们要学习组件的生命周期钩子函数

生命周期钩子函数的使用可以帮助我们更好的控制和管理组件

2. React生命周期阶段

我们将React生命周期分为四个阶段,分别如下:

  1. 挂载阶段
  2. 更新阶段
  3. 卸载阶段
  4. 错误处理阶段【 React16.3版本之后新增 】

3. React生命周期注意点

  1. React16废弃的三个生命周期函数

    • componentWillMount
    • componentWillReceiveProps
    • componentWillUpdate

      目前在16版本中componentWillMount,componentWillReceiveProps,componentWillUpdate并未完全删除这三个生命周期函数,而且新增了UNSAFEcomponentWillMount,UNSAFE_componentWillReceiveProps,UNSAFE_componentWillUpdate三个函数,官方计划在17版本完全删除这三个函数,只保留UNSAVE前缀的三个函数,目的是为了向下兼容,但是对于开发者而言应该尽量避免使用他们,而是使用新增的生命周期函数替代它们

  2. 取而代之的是两个新的生命周期函数

    • static getDerivedStateFromProps
    • getSnapshotBeforeUpdate

3. React生命周期图示

4. React生命周期各阶段钩子函数解析

初始化阶段

初始化阶段就是将我们的组件插入到DOM中,只会发生一次

  • 这个阶段的生命周期函数调用如下:
    • constructor
    • getDerivedStateFromProps
    • componentWillMount/UNSAVE_componentWillMount
    • render
    • componentDidMount
  1. constructor 构造器函数

    1. 触发时间: React组件的构造函数在挂载之前被调用
    2. 作用:
      • 调用super(props),用来将父组件传来的props绑定到这个类中,使用this.props将会得到
      • 做一些初始化的操作,比如: state的初始化、非箭头函数的实例属性的this绑定
    3. 说明
      • 禁止在constructor中调用setState,可以直接给state赋初始值
    4. 代码演示

       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>
           )
         }
       }
      
  2. getDerivedStateFromProps

    1. 调用时间: 该函数会在挂载时,接收到新的props,调用了setState和forceUpdate时被调用
    2. 作用:
      • 一个静态方法,所以不能在这个函数里面使用this,这个函数有两个参数props和state,分别指接收到的新参数和当前的state对象,这个函数会返回一个对象用来更新当前的state对象,如果不需要更新可以返回null
    3. 代码演示
        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
          }
        }
      
  3. componentWillMount OR UNSAFE_componentWillMount

    在16版本这两个方法并存,但是在17版本中componentWillMount被删除,只保留UNSAFE_componentWillMount,目的是为了做向下兼容,对于新的应用,用getDerivedStateFromProps代替它们由于componentWillMount/ UNSAFE_componentWillMount是在render之前调用,所以就算在这个方法中调用setState也不会触发重新渲染(re-render)

  4. render 函数

    React中最核心的方法,一个组件中必须要有这个方法

    1. 触发时间:初始化阶段和更新阶段都会触发
    2. 作用:
      • 返回需要渲染的vdom
      • 进行this.props && this.state 的计算
    3. 说明
      • render函数式必须要有返回值的,返回值的数据类型可以为:
        • React元素
        • React组件
        • Fragment( 片段 )
        • Portals【 插槽 】
        • 字符串或是数组,被渲染成text节点
        • Boolean和null,不会渲染任何东西
    4. 代码演示
       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>
           )
         }
       }
      
  5. componentDidMount

    1. 调用时间: 组件挂载结束之后调用
    2. 作用
      • 可以做数据请求,并将请求之后的结果赋值给组件的state
      • 可以进行DOM操作
      • 可以进行第三方库的实例化
    3. 代码演示

      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
  1. componentWillReceiveProps/UNSAFE_componentWillReceiveProps

    在16版本这两个方法并存,但是在17版本中componentWillReceiveProps被删除,UNSAFE_componentWillReceiveProps,目的是为了做向下兼容,对于新的应用,用getDerivedStateFromProps代替它们

    1. 触发条件: 组件的props发生改变时触发
    2. 作用
      • 用于接收新的属性,然后做一些props改变的监听
    3. 代码演示
      import React from 'react'
      class Hello extends Component{
        componentWillReceiveProps( nextProps ) {
          console.log('nextProps',nextProps) //用于接收变化后的新的属性
        }
        render () {
          return (
            <div></div>
          )
        }
      }
      
  2. getDerivedStateFromProps

    初始化阶段已经解释过,这里就不在多解释

  3. shouldComponentUpdate( nextProps,nextState )

    1. 有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true
    2. 因为默认是返回true,也就是只要接收到新的属性和调用了setState都会触发重新的渲染,这会带来一定的性能问题,所以我们需要将this.props与nextProps以及this.state与nextState进行比较来决定是否返回false,来减少重新渲染
    3. 注意
      • 但是官方提倡我们使用PureComponent来减少重新渲染的次数而不是手工编写shouldComponentUpdate代码
    4. 代码演示
        import React from 'react'
        class Hello extends React.Component{
          shouldComponentUpdate ( nextProps,nextState ) {
            return this.props.xxx == nextProps.xxx
          }
          render () {
            return (
              <div></div>
            )
          }
        }
      
  4. componentWillUpdate/UNSAFE_componentWillUpdate

    • 在16版本这两个方法并存,但是在17版本中componentWillUpdate被删除,UNSAFE_componentWillUpdate,目的是为了做向下兼容 在这个方法里,你不能调用setState,因为能走到这个方法,说明shouldComponentUpdate返回true,此时下一个state状态已经被确定,马上就要执行render重新渲染了,否则会导致整个生命周期混乱,在这里也不能请求一些网络数据,因为在异步渲染中,可能会导致网络请求多次,引起一些性能问题,
    • 如果你在这个方法里保存了滚动位置,也是不准确的,还是因为异步渲染的问题,如果你非要获取滚动位置的话,请在getSnapshotBeforeUpdate调用
  5. render

    更新阶段也会再次出发

  6. getSnapshotBeforeUpdate(prevProps, prevState)

    1. 调用时间: render结束之后,componentDidUpdate调用之前
    2. 作用
      • 这个函数有一个返回值,会作为第三个参数传递给componentDidUpdate
    3. 注意
      • 这个方法一定要和componentDidUpdate一起使用,否则控制台也会有警告
    4. 代码演示
        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)
          }
        }
      
  7. componentDidUpdate(prevProps, prevState, snapshot)
    1. 调用时间: getSnapshotBeforeUpdter调用之后
    2. 作用
      • 这个函数可以通过snapshot参数来接收getSnapshotVeforeUpdater的返回值
      • 进行DOM操作
      • 发起服务器请求
      • 进行第三方库实例化
    3. 注意
      • 这个方法还可以setState,但是注意一定要用if语句控制,否则会导致无限循环
    4. 代码演示
        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)
          }
        }
      
卸载阶段
  1. 触发时间: 当我们的组件被卸载或者销毁了就会调用,
  2. 作用:
    • 我们可以在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作
  3. 代码演示
     import React from 'react'
     class Hello extends React.Component{
       render () {
         return (
           <div></div>
         )
       }
       componentWillUnmount () {
         clearInterval( this.timer )
         window.onscroll = null 
       }
     }
    
错误处理阶段
  1. componentDidCatch(error, info)

    1. 调用: 此生命周期在后代组件抛出错误后被调用。 它接收两个参数:
      • error —— 抛出的错误。
      • info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。
    2. 作用:
      • componentDidCatch() 会在“提交”阶段被调用,因此允许执行副作用。 它应该用于记录错误之类的情况:
      • 错误边界是React组件,可以在其子组件树中的任何位置捕获JavaScript错误,记录这些错误并显示回退UI,而不是崩溃的组件树。错误边界在渲染期间,生命周期方法以及整个树下的构造函数中捕获错误
    3. 代码演示
     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; 
       }
     }
    

results matching ""

    No results matching ""