我总觉得react就像一个状态机,它提供的生命周期让我能够在某个特定时间做特定的事情,得到我想要的结果。

react16.3对生命周期的upgrade

React16打算废弃的三个生命周期函数

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

官方说法是:官方计划在17版本完全删除这三个函数,只保留UNSAVE_前缀的三个函数,目的是为了向下兼容,但是对于开发者而言应该尽量避免使用他们,而是使用新增的生命周期函数替代它们

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

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

生命周期概述

constructor
  1. 用于初始化内部状态
  2. 唯一可以直接修改state的地方(不需要setState)
getSnapshotBeforeUpdate
  1. 在页面render之前调用
  2. 典型场景:在render之前,获取上一次render的dom状态
getDerivedStateFromProps
  1. 当state需要从props初始化时使用
  2. 每次render都会调用
  3. 典型场景: 表单控件从父组件获取默认值
  4. 注意事项: 官方不推荐经常使用
componentDidMount
  1. UI渲染完成后调用
  2. 只执行一次
  3. 典型场景: 获取外部数据
componentWillUnMount
  1. 组件被移除时调用
  2. 典型场景:资源释放

详谈sholudComponentUpdate

shouldComponentUpdate(nextProps, nextState)

shouldComponentUpdate是pureComponent里面实现的方法,它用于比较属性的值有没有发生改变,如果没有的话就不render了。
也就是说
component: 可以自己实现shouldComponentUpdate
pureComponent:已经帮我们实现了,要注意是浅比较。

这里由于arr是一个引用数据类型,它的地址没有发生改变,所以我们的PureComponent是不会触发render的。你可以把PureComponent改成component就会发现能够re-render.

import React, { PureComponent, Component } from 'react';

class IndexPage extends PureComponent {
  constructor() {
    super();
    this.state = {
      arr:['1']
    };
    console.log('constructor');
  }

  changeState = () => {
    let { arr } = this.state;
    arr.push('2');
    console.log(arr);
    this.setState({
      arr
    })
  };
  
  render() {
    // if the component was extended by the component, it will re-render each time you click
    // if the component was extended by the purecomponent, it won't re-render if the value didn't change
    // if the value is an array, the location of arr doesn't change means the value doesn't change
    console.log('render'); 
    return (
      <div>
        <button onClick={this.changeState}>点击</button>
        <div>
          {this.state.arr.map((item) => {
            return item;
          })}
        </div>
      </div>
    );
  }
}

export default IndexPage

当然,我们可以比较轻松地解决这个问题,就是new一个数组来赋值。

  this.setState({
      arr: [...arr] // create a new array, so it would re-render
  })

我认为比较好的方式是:父组件用PureComponent, 子组件用Component,保证子组件能够follow父组件的变化。

一点疑惑: 从后端获取数据 componentWillMount vs componentDidMount

首先对比一下两个生命周期获取数据的区别:
componentWillMount获取数据:

componentWillMount:
1. 执行willMount函数,等待数据返回

2. 执行render函数

3. 执行didMount函数

4. 数据返回, 执行render

componentDidMount:
1. 执行willMount函数

2. 执行render函数

3. 执行didMount函数, 等待数据返回

4. 数据返回, 执行render
看起来,我们在componentWillMount里面获取数据是可以更节约时间的。
关于为什么废弃它, 官方说法是这样的:

UNSAFE_componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering. Generally, we recommend using the constructor() instead for initializing state.

Avoid introducing any side-effects or subscriptions in this method. For those use cases, use componentDidMount() instead.

side-effects说的可能是:

  1. 如果使用服务端渲染的话,willMount会在服务端和客户端各自执行一次,这会导致请求两次(接受不了~),而didMount只会在客户端进行。
  2. 在Fiber之后, 由于任务可中断,willMount可能会被执行多次。

节省一次render时间,我想如果我们在render里面判断数据没有ready返回null或者一个Loading图片,也是可以做到的。


supportlss
230 声望16 粉丝