我对 React v16.4 生命周期的理解

关于react生命周期的文章,网上一大堆,本人也看了许多,可是以为大部分人写的都是照搬其它人的没有本身独到的看法,因此决定根据本人的实战经验和我的理解再写一篇React生命周期的文章,因为React目前已更新到16.4版本,因此重点讲解React v16.4变化的生命周期,以前的生命周期函数会一带而过react

先整体看下React16的生命周期图canvas

React16废弃的三个生命周期函数浏览器

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

注:目前在16版本中componentWillMountcomponentWillReceivePropscomponentWillUpdate并未彻底删除这三个生命周期函数,并且新增了UNSAFE_componentWillMountUNSAFE_componentWillReceivePropsUNSAFE_componentWillUpdate三个函数,官方计划在17版本彻底删除这三个函数,只保留UNSAVE_前缀的三个函数,目的是为了向下兼容,可是对于开发者而言应该尽可能避免使用他们,而是使用新增的生命周期函数替代它们bash

取而代之的是两个新的生命周期函数服务器

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

咱们将React的生命周期分为三个阶段,而后详细讲解每一个阶段具体调用了什么函数,这三个阶段是:微信

  • 挂载阶段
  • 更新阶段
  • 卸载阶段

挂载阶段

挂载阶段,也能够理解为组件的初始化阶段,就是将咱们的组件插入到DOM中,只会发生一次网络

这个阶段的生命周期函数调用以下:app

  • constructor
  • getDerivedStateFromProps
  • componentWillMount/UNSAVE_componentWillMount
  • render
  • componentDidMount

constructor

组件构造函数,第一个被执行异步

若是没有显示定义它,咱们会拥有一个默认的构造函数svg

若是显示定义了构造函数,咱们必须在构造函数第一行执行super(props),不然咱们没法在构造函数里拿到this对象,这些都属于ES6的知识

在构造函数里面咱们通常会作两件事:

  • 初始化state对象
  • 给自定义方法绑定this
constructor(props) {
    super(props)
    
    this.state = {
      select,
      height: 'atuo',
      externalClass,
      externalClassText
    }

    this.handleChange1 = this.handleChange1.bind(this)
    this.handleChange2 = this.handleChange2.bind(this)
}
复制代码

禁止在构造函数中调用setState,能够直接给state设置初始值

getDerivedStateFromProps

static getDerivedStateFromProps(nextProps, prevState)

一个静态方法,因此不能在这个函数里面使用this,这个函数有两个参数props和state,分别指接收到的新参数和当前的state对象,这个函数会返回一个对象用来更新当前的state对象,若是不须要更新能够返回null

该函数会在挂载时,接收到新的props,调用了setState和forceUpdate时被调用

在React v16.3时只有在挂载时和接收到新的props被调用,听说这是官方的失误,后来修复了

这个方法就是为了取代以前的componentWillMountcomponentWillReceivePropscomponentWillUpdate

当咱们接收到新的属性想去修改咱们state,可使用getDerivedStateFromProps

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/UNSAFE_componentWillMount

在16版本这两个方法并存,可是在17版本中componentWillMount被删除,只保留UNSAFE_componentWillMount,目的是为了作向下兼容,对于新的应用,用getDerivedStateFromProps代替它们

因为componentWillMount/ UNSAFE_componentWillMount是在render以前调用,因此就算在这个方法中调用setState也不会触发从新渲染(re-render)

render

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

返回的类型有如下几种:

  • 原生的DOM,如div
  • React组件
  • Fragment(片断)
  • Portals(插槽)
  • 字符串和数字,被渲染成text节点
  • Boolean和null,不会渲染任何东西

关于Fragment和Portals是React16新增的,若是你们不清楚能够去阅读官方文档,在这里就不展开了

render函数是纯函数,里面只作一件事,就是返回须要渲染的东西,不该该包含其它的业务逻辑,如数据请求,对于这些业务逻辑请移到componentDidMount和componentDid Update中

componentDidMount

组件装载以后调用,此时咱们能够获取到DOM节点并操做,好比对canvas,svg的操做,服务器请求,订阅均可以写在这个里面,可是记得在componentWillUnmount中取消订阅

componentDidMount() {
    const { progressCanvas, progressSVG } = this

    const canvas = progressCanvas.current
    const ctx = canvas.getContext('2d')
    canvas.width = canvas.getBoundingClientRect().width
    canvas.height = canvas.getBoundingClientRect().height

    const svg = progressSVG.current
    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
    rect.setAttribute('x', 0)
    rect.setAttribute('y', 0)
    rect.setAttribute('width', 0)
    rect.setAttribute('height', svg.getBoundingClientRect().height)
    rect.setAttribute('style', 'fill:red')

    const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate')
    animate.setAttribute('attributeName', 'width')
    animate.setAttribute('from', 0)
    animate.setAttribute('to', svg.getBoundingClientRect().width)
    animate.setAttribute('begin', '0ms')
    animate.setAttribute('dur', '1684ms')
    animate.setAttribute('repeatCount', 'indefinite')
    animate.setAttribute('calcMode', 'linear')
    rect.appendChild(animate)
    svg.appendChild(rect)
    svg.pauseAnimations()

    this.canvas = canvas
    this.svg = svg
    this.ctx = ctx
 }
复制代码

在componentDidMount中调用setState会触发一次额外的渲染,多调用了一次render函数,可是用户对此没有感知,由于它是在浏览器刷新屏幕前执行的,可是咱们应该在开发中避免它,由于它会带来必定的性能问题,咱们应该在constructor中初始化咱们的state对象,而不该该在componentDidMount调用state方法

更新阶段

更新阶段,当组件的props改变了,或组件内部调用了setState或者forceUpdate发生,会发生屡次

这个阶段的生命周期函数调用以下:

  • componentWillReceiveProps/UNSAFE_componentWillReceiveProps
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • componentWillUpdate/UNSAFE_componentWillUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate

componentWillReceiveProps/UNSAFE_componentWillReceiveProps

componentWillReceiveProps(nextProps, prevState) UNSAFE_componentWillReceiveProps(nextProps, prevState)

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

注意,当咱们父组件从新渲染的时候,也会致使咱们的子组件调用componentWillReceiveProps/UNSAFE_componentWillReceiveProps,即便咱们的属性和以前的同样,因此须要咱们在这个方法里面去进行判断,若是先后属性不一致才去调用setState

在装载阶段这两个函数不会被触发,在组件内部调用了setState和forceUpdate也不会触发这两个函数

getDerivedStateFromProps

这个方法在装载阶段已经讲过了,这里再也不赘述,记住在更新阶段,不管咱们接收到新的属性,调用了setState仍是调用了forceUpdate,这个方法都会被调用

shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState)

有两个参数nextProps和nextState,表示新的属性和变化以后的state,返回一个布尔值,true表示会触发从新渲染,false表示不会触发从新渲染,默认返回true

注意当咱们调用forceUpdate并不会触发此方法

由于默认是返回true,也就是只要接收到新的属性和调用了setState都会触发从新的渲染,这会带来必定的性能问题,因此咱们须要将this.props与nextProps以及this.state与nextState进行比较来决定是否返回false,来减小从新渲染

可是官方提倡咱们使用PureComponent来减小从新渲染的次数而不是手工编写shouldComponentUpdate代码,具体该怎么选择,全凭开发者本身选择

在将来的版本,shouldComponentUpdate返回false,仍然可能致使组件从新的渲染,这是官方本身说的

Currently, if shouldComponentUpdate() returns false, then UNSAFE_componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.

componentWillUpdate/UNSAFE_componentWillUpdate

componentWillUpdate(nextProps, nextState)
UNSAFE_componentWillUpdate(nextProps, nextState)

在16版本这两个方法并存,可是在17版本中componentWillUpdate被删除,UNSAFE_componentWillUpdate,目的是为了作向下兼容

在这个方法里,你不能调用setState,由于能走到这个方法,说明shouldComponentUpdate返回true,此时下一个state状态已经被肯定,立刻就要执行render从新渲染了,不然会致使整个生命周期混乱,在这里也不能请求一些网络数据,由于在异步渲染中,可能会致使网络请求屡次,引发一些性能问题,

若是你在这个方法里保存了滚动位置,也是不许确的,仍是由于异步渲染的问题,若是你非要获取滚动位置的话,请在getSnapshotBeforeUpdate调用

render

更新阶段也会触发,装载阶段已经讲过了,再也不赘述

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps, prevState)

这个方法在render以后,componentDidUpdate以前调用,有两个参数prevProps和prevState,表示以前的属性和以前的state,这个函数有一个返回值,会做为第三个参数传给componentDidUpdate,若是你不想要返回值,请返回null,不写的话控制台会有警告

还有这个方法必定要和componentDidUpdate一块儿使用,不然控制台也会有警告

前面说过这个方法时用来代替componentWillUpdate/UNSAVE_componentWillUpdate,下面举个例子说明下:

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the scroll position so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    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.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}
复制代码

componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot)

该方法在getSnapshotBeforeUpdate方法以后被调用,有三个参数prevProps,prevState,snapshot,表示以前的props,以前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的

在这个函数里咱们能够操做DOM,和发起服务器请求,还能够setState,可是注意必定要用if语句控制,不然会致使无限循环

卸载阶段

卸载阶段,当咱们的组件被卸载或者销毁了

这个阶段的生命周期函数只有一个:

  • componentWillUnmount

componentWillUnmount

当咱们的组件被卸载或者销毁了就会调用,咱们能够在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工做

注意不要在这个函数里去调用setState,由于组件不会从新渲染了

最后

查看 React v16.4 生命周期

大家的打赏是我写做的动力

微信
支付宝
相关文章
相关标签/搜索