资讯专栏INFORMATION COLUMN

React 生命周期

Dean / 1097人阅读

摘要:同步渲染的痛点当应用的组件树特别庞大时,由于是单线程的,重新渲染一旦开始,中间不会停,如果这时候用户去操作,比如输入,点击按钮,此时页面是没有响应的。

React生命周期 基础生命周期钩子

constructor

如果你不初始化状态,也不绑定方法,那么你就不需要为React组件实现构造函数。在这里初始化状态可以直接对this.state赋值,在这里使用props时,应当通过this.props使用;如果组件内定义的方法不是采用箭头函数默认绑定到组件实例上时,调用时应该用bind手动绑定。

componentWillMount(将要废弃)

在react17版本前有效的生命周期钩子,不能与新版钩子混用(componentWillRecevieProps、componentWillUpdate也是如此);
有一种常见的误区是在componentWillMount中请求数据可以避免第一次empty render,但实际中render在componentWillMount后立即执行,如果componentWillMount拉取的数据不能立即得到,那么二次渲染依然无法避免,这种情况下推荐在componentDidMount中请求数据。
所以总的来说没啥用处。

render

render()方法是类组件唯一必须的方法,在该钩子内,将React JSX 渲染为DOM节点。
render()函数应该是纯的,意味着不应该改变组件的状态,其每次调用都应返回相同的结果,同时它不会直接和浏览器交互

componentDidMount

在该钩子内,render生成的DOM节点挂载到DOM树中,在这里你可以通过ref取到DOM节点,有些需求下,可能需要DOM节点挂载后才能取到相应的属性,例如DOM节点的尺寸等,你可以立即调用setState()。它将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了在此情况下即使render()将会调用两次,用户也不会看到中间状态。

componentWillUnmount

componentWillUnmount()紧挨着在组件被卸载和销毁之前调用。可以在该方法里处理任何必要的清理工作,例如解绑定时器,取消网络请求,清理任何在componentDidMount环节创建的订阅。

稍微进阶的生命周期钩子

上述介绍的基本生命周期钩子是上图的左半部分,走完了一次初始渲染的流程,但React组件嵌套复用,当父组件更新或者组件自身状态更新触发子组件重新渲染时,合理的使用上图右半部分的生命周期钩子可以有效提升性能。

componentWillReceiveProps(nextProps)(将要废弃)

componentWillReceiveProps()在装载了的组件接收到新属性前调用。可能当接收到新属性后,通过if比较新旧Props不同,想要通过this.setState()来更新组件的状态。

shouldComponentUpdate(nextProps, nextState)

当接收到新属性或状态时,shouldComponentUpdate() 在渲染前被调用。默认为true。该方法在初始化渲染或当使用forceUpdate()时并不会被调用。
适用场景是:你想在某些状态或属性变化时,通过this.props和nextProps以及this.state 和 nextState比较,来判断是否需要重新渲染该组件,return fasle 不渲染,默认为true。不能setState。
有内建的React.PureComponent代替手写shouldComponentUpdate()。PureComponent 对属性和状态执行浅比较,因而降低你略过必要更新的机会。
但如果你需要更精细的控制,那就自定义shouldComponentUpdate吧。
但是当你的state或props是嵌套很深的引用类型时,容易由于浅比较而出错,而React文档说我们不推荐做深相等检测,或使用JSON.stringify()在shouldComponentUpdate()中,这是非常无效率的会伤害性能。所以你最好设计好自己的数据结构,不要让数据嵌套太深。

componentWillUpdate(nextProps, nextState)

类似于componentWillMount,在nextProps, nextState生效前最后的一次准备机会,可以读取DOM属性,为componentDidUpdate作准备(使用场景少)。不能setState。

componentDidUpdate

组件更新后触发,类似于componentDidMount的作用,可以在这里操作DOM,发起请求等。

参考链接:
React Lifecycle Methods- how and when to use them
React组件生命周期小结

React v16.3新出的生命周期钩子

static getDerivedStateFromProps(nextProps, prevState)

与旧的生命周期图比较可以看出,getDerivedStateFromProps处于原来的componentWillMountcomponentWillReceiveProps位置,而且this.setState()以及this.forceUpdate()也会触发该钩子。
该钩子的主要作用是为了替代componentWillReceiveProps作用。

// Before
class ExampleComponent extends React.Component {
  state = {
    isScrollingDown: false,
  };
  componentWillReceiveProps(nextProps) {
    if (this.props.currentRow !== nextProps.currentRow) {
      this.setState({
        isScrollingDown:
          nextProps.currentRow > this.props.currentRow,
      });
    }
  }
}
// After
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 to indicate no change to state.
    return null;
  }
}

与原有钩子相比,getDerivedStateFromProps是一个静态方法,无法访问组件实例,如此限制了其使用范围,防止不熟练的开发者自由发挥导致各种bug。该钩子主要作用可以概括为接收props来改变state。

可以发现在使用getDerivedStateFromProps时,我们在state中多花了一个变量lastRow来保存prevProps,为什么设计钩子的时候不在参数值加入prevProps呢? 官方文档的解释是:1.首次渲染也会触发getDerivedStateFromProps,这意味着每次都需要对prevProps进行空置检测,麻烦;2.不将prevProps传入是为了节省内存。

getSnapshotBeforeUpdate(prevProps, prevState)

取代componenWillUpdate功能。
getSnapshotBeforeUpdate在最新的渲染输出提交给DOM前将会立即调用。它让你的组件能在当前的值可能要改变前获得它们。这一生命周期返回的任何值将会 作为参数被传递给componentDidUpdate()。

class ScrollingList extends React.Component {
  listRef = null;

  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.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.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      this.listRef.scrollTop +=
        this.listRef.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      
{/* ...contents... */}
); } setListRef = ref => { this.listRef = ref; }; }

static getDerivedStateFromError()componentDidCatch()

错误处理。。。
render phase 里产生异常的时候, 会调用 getDerivedStateFromError;

在 commit phase 里产生异常大的时候, 会调用 componentDidCatch。

componentDidCatch 是不会在服务器端渲染的时候被调用的 而getDerivedStateFromError 会

为何用推出新的钩子,废弃旧的钩子?

主要是为了适应将要推出的异步渲染,其次是避免之前生命周期钩子的滥用。

同步渲染与异步渲染

React Fiber 是在 v16 的时候引入的一个全新架构, 旨在解决异步渲染问题,但v16并没有开启。
同步渲染的痛点:当应用的组件树特别庞大时,由于javaScript是单线程的,重新渲染一旦开始,中间不会停,如果这时候用户去操作, 比如输入, 点击按钮, 此时页面是没有响应的。 等更新完了, 你之前的那些输入就会啪啪啪一下子出来了。函数调用栈如图所示:

因为JavaScript单线程的特点,每个同步任务不能耗时太长,不然就会让程序不会对其他输入作出相应,React的更新过程就是犯了这个禁忌,而React Fiber就是要改变现状。

Fiber 的做法是:分片。
把一个很耗时的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。 而维护每一个分片的数据结构, 就是Fiber。
用一张图来展示Fiber 的碎片化更新过程

在React Fiber中,一次更新过程会分成多个分片完成,所以完全有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断,这时候,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。

因为一个更新过程可能被打断,所以React Fiber一个更新过程被分为两个阶段: render phase and commit phase.(可以从上文的生命周期图中看到)

因为第一阶段的过程会被打断而且“重头再来”,就会造成意想不到的情况。

比如说,一个低优先级的任务A正在执行,已经调用了某个组件的componentWillUpdate函数,接下来发现自己的时间分片已经用完了,于是冒出水面,看看有没有紧急任务,哎呀,真的有一个紧急任务B,接下来React Fiber就会去执行这个紧急任务B,任务A虽然进行了一半,但是没办法,只能完全放弃,等到任务B全搞定之后,任务A重头来一遍,注意,是重头来一遍,不是从刚才中段的部分开始,也就是说,componentWillUpdate函数会被再调用一次。

在现有的React中,每个生命周期函数在一个加载或者更新过程中绝对只会被调用一次;在React Fiber中,不再是这样了,第一阶段中的生命周期函数在一次加载和更新过程中可能会被多次调用!。

总而言之就是:
render phase 可以被打断, 大家不要在此阶段做一些有副作用的操作,可以放心在commit phase 里做。

然后就是生命周期的调整, react 把你有可能在render phase 里做的有副作用的函数都改成了static 函数, 强迫开发者做一些纯函数的操作。

全面了解 React 新功能: Suspense 和 Hooks

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109090.html

相关文章

  • React专题:生命周期

    摘要:而生命周期钩子,就是从生到死过程中的关键节点。异步渲染下的生命周期花了两年时间祭出渲染机制。目前为这几个生命周期钩子提供了别名,分别是将只提供别名,彻底废弃这三个大活宝。生命周期钩子的最佳实践是在这里初始化。 本文是『horseshoe·React专题』系列文章之一,后续会有更多专题推出来我的 GitHub repo 阅读完整的专题文章来我的 个人博客 获得无与伦比的阅读体验 生命周期...

    Hanks10100 评论0 收藏0
  • 捋一捋React生命周期

    摘要:卸载阶段组件卸载和销毁老版生命周期之前的生命周期初始化阶段涉及个钩子函数这些方法会在组件初始化的时候被调用,只跟实例的创建有关。 前言:React 的版本从 v15 到 v16.3 ,再到v16.4,现在最新的版本是 v16.8了。其中最大的变化可能是React Hooks的加入,而最令人困惑的却是它的生命周期,新旧生命周期函数混杂在一起,难免会让许多新来者有很多困惑。所以这一篇我们来...

    MobService 评论0 收藏0
  • ReactV16.3,即将更改的生命周期

    摘要:我们目前的计划是为不安全生命周期引入别名,和。从现在开始,只有新的生命周期名称将起作用。从版本开始,更新以响应更改的推荐方法是使用新的静态生命周期。 注释:本文是根据React的官方博客翻译而成(文章地址:https://reactjs.org/blog/2018...)。主要讲述了React之后的更新方向,以及对之前生命周期所出现的问题的总结,之后的React将逐步弃用一些生命周期和...

    wendux 评论0 收藏0
  • React组件生命周期详解

    摘要:组件生命周期构造方法是对类的默认方法,通过命令生成对象实例时自动调用该方法。该生命周期可以发起异步请求,并。后废弃该生命周期,可以在中完成设置渲染组件是一个组件必须定义的生命周期,用来渲染。该生命周期内可以进行。 React组件生命周期 constructor( ) 构造方法 constructor是ES6对类的默认方法,通过 new 命令生成对象实例时自动调用该方法。并且,该方法是...

    learn_shifeng 评论0 收藏0
  • React.js 小书 Lesson20 - 更新阶段的组件生命周期

    摘要:所以对于组件更新阶段的组件生命周期,我们简单提及并且提供一些资料给大家。这里为了知识的完整,补充关于更新阶段的组件生命周期你可以通过这个方法控制组件是否重新渲染。大家对这更新阶段的生命周期比较感兴趣的话可以查看官网文档。 React.js 小书 Lesson20 - 更新阶段的组件生命周期 本文作者:胡子大哈本文原文:http://huziketang.com/books/react...

    Yumenokanata 评论0 收藏0
  • React组件生命周期

    摘要:组件生命周期此文章适合之前的版本,,添加了一些新的生命周期函数,同时准备废弃一些会造成困扰的生命周期函数。每个生命周期阶段调用的钩子函数会略有不同。 React组件生命周期 此文章适合 React@17 之前的版本,React@16.3.0,添加了一些新的生命周期函数,同时准备废弃一些会造成困扰的生命周期函数。所有如果在React@17 发布之前,这篇文章还是适用的。新的生命周期请看官...

    mgckid 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<