react v16.5.2 生命周期方法 + 图解

参考:

https://blog.csdn.net/c_kite/article/details/80303341

官网:https://react.docschina.org/docs/react-component.html

 

三个UNSAFE_开头的方法是将三个弃用方法向下兼容,不能使用新的组建API,在17版本前名字还都有效,是不推荐使用的

装载过程:

1.constructor()

constructor(props)

React组件的构造函数将会在装配之前被调用。当为一个React.Component子类定义构造函数时,你应该在任何其他的表达式之前调用super(props)。否则,this.props在构造函数中将是未定义,并可能引发异常。

 

构造函数是初始化状态的合适位置。若你不初始化状态且不绑定方法,那你也不需要为你的React组件定义一个构造函数。(所以,原来在UNSAFE_componentWillMount里请求初始化数据,现在写在这里)

可以基于属性来初始化状态。这样有效地“分离(forks)”属性并根据初始属性设置状态。这有一个有效的React.Component子类构造函数的例子:

constructor(props) {
  super(props);
  this.state = {
    color: props.initialColor
  };
}

当心这种模式,因为状态将不会随着属性的更新而更新。保证属性和状态同步,你通常想要状态提升

若你通过使用它们为状态“分离”属性,你可能也想要实现UNSAFE_componentWillReceiveProps(nextProps)以保持最新的状态。但状态提升通常来说更容易以及更少的异常。


2.static getDerivedStateFromProps()

static getDerivedStateFromProps(nextProps, prevState)

组件实例化后和接受新属性时将会调用getDerivedStateFromProps。它应该返回一个对象来更新状态,或者返回null来表明新属性不需要更新任何状态。

注意,如果父组件导致了组件的重新渲染,即使属性没有更新,这一方法也会被调用。如果你只想处理变化,你可能想去比较新旧值。

 

调用this.setState() 通常不会触发 getDerivedStateFromProps()

 

注:static静态方法,所以在这个方法中使用this并不表示本组件


3.UNSAFE_componentWillMount()

UNSAFE_componentWillMount()

UNSAFE_componentWillMount()在装配发生前被立刻调用。其在render()之前被调用,因此在这方法里同步地设置状态将不会触发重渲。

 

避免在该方法中引入任何的副作用或订阅。对于这些使用场景,我们推荐使用constructor()来替代。

这是唯一的会在服务端渲染调起的生命周期钩子函数。

注意

这一生命周期之前叫做componentWillMount。这一名字在17版前都有效。可以使用rename-unsafe-lifecycles codemod来自动更新你的组件。


5.componentDidMount()

componentDidMount()

 

componentDidMount()在组件被装配后立即调用。初始化使得DOM节点应该进行到这里。若你需要从远端加载数据,这是一个适合实现网络请求的地方。在该方法里设置状态将会触发重渲。

(请求异步加载数据)

这一方法是一个发起任何订阅的好地方。如果你这么做了,别忘了在componentWillUnmount()退订。

 

在这个方法中调用setState()将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了即使render()将会调用两次,但用户不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。然而,它对于像模态框和工具提示框这样的例子是必须的。这时,在渲染依赖DOM节点的尺寸或者位置的视图前,你需要先测量这些节点。


更新过程:

6.UNSAFE_componentWillReceiveProps()

UNSAFE_componentWillReceiveProps(nextProps)

注意

推荐你使用getDerivedStateFromProps生命周期而不是UNSAFE_componentWillReceiveProps关于此建议在此了解详情。

UNSAFE_componentWillReceiveProps()在装配了的组件接收到新属性前调用。若你需要更新状态响应属性改变(例如,重置它),你可能需对比this.propsnextProps并在该方法中使用this.setState()处理状态改变。

注意即使属性未有任何改变,React可能也会调用该方法,因此若你想要处理改变,请确保比较当前和之后的值。这可能会发生在当父组件引起你的组件重渲。

在 装配期间,React并不会调用带有初始属性的UNSAFE_componentWillReceiveProps方法。其仅会调用该方法如果某些组件的属性可能更新。调用this.setState通常不会触发UNSAFE_componentWillReceiveProps

注意

这一生命周期之前叫做componentWillReceiveProps。这一名字在17版前都有效。可以使用rename-unsafe-lifecycles codemod来自动更新你的组件。


7.shouldComponentUpdate()

shouldComponentUpdate(nextProps, nextState)

使用shouldComponentUpdate()以让React知道当前状态或属性的改变是否不影响组件的输出。默认行为是在每一次状态的改变重渲,在大部分情况下你应该依赖于默认行为。

当接收到新属性或状态时,shouldComponentUpdate() 在渲染前被调用。默认为true。该方法并不会在初始化渲染或当使用forceUpdate()时被调用。

当他们状态改变时,返回false 并不能阻止子组件重渲。

当前,若shouldComponentUpdate()返回false,而后UNSAFE_componentWillUpdate()render(), 和 componentDidUpdate()将不会被调用。注意,在未来React可能会将shouldComponentUpdate()作为一个线索而不是一个严格指令,返回false可能仍然使得组件重渲。

在观察后,若你判定一个具体的组件很慢,你可能需要调整其从React.PureComponent继承,其实现了带有浅属性和状态比较的shouldComponentUpdate()。若你确信想要手写,你可能需要用this.propsnextProps以及this.state 和 nextState比较,并返回false以告诉React更新可以被忽略。


8.UNSAFE_componentWillUpdate()

UNSAFE_componentWillUpdate(nextProps, nextState)

当接收到新属性或状态时,UNSAFE_componentWillUpdate()为在渲染前被立即调用。在更新发生前,使用该方法是一次准备机会。该方法不会在初始化渲染时调用。

注意你不能在这调用this.setState(),若你需要更新状态响应属性的调整,使用getDerivedStateFromProps() 代替。

注意

这一生命周期之前叫做componentWillUpdate。这一名字在17版前都有效。可以使用rename-unsafe-lifecycles codemod来自动更新你的组件。

注意

shouldComponentUpdate()返回false,UNSAFE_componentWillUpdate()将不会被调用。


9.getSnapshotBeforeUpdate()

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

例如:

 

class ScrollingList extends React.Component {
  listRef = React.createRef();

  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.current.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.
    if (snapshot !== null) {
      this.listRef.current.scrollTop +=
        this.listRef.current.scrollHeight - snapshot;
    }
  }

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

 

 

在上面的例子中,为了支持异步渲染,在getSnapshotBeforeUpdate 中读取scrollHeight而不是componentWillUpdate,这点很重要。由于异步渲染,在“渲染”时期(如componentWillUpdaterender和“提交”时期(如getSnapshotBeforeUpdatecomponentDidUpdate间可能会存在延迟。如果一个用户在这期间做了像改变浏览器尺寸的事,从componentWillUpdate中读出的scrollHeight值将是滞后的。

 

(理解:componentWillUpdate 的用例是在组件更新前,读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。但在 React 开启异步渲染模式后,render 阶段和 commit 阶段之间并不是无缝衔接的,也就是说在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在 

componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。解决的办法就是getSnapshotBeforeUpdate 。与 componentWillUpdate 不同,getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。虽然 getSnapshotBeforeUpdate 不是一个静态方法,但我们也应该尽量使用它去返回一个值。这个值会随后被传入到 componentDidUpdate中,然后我们就可以在 componentDidUpdate 中去更新组件的状态,而不是在 getSnapshotBeforeUpdate 中直接更新组件状态。


10.componentDidUpdate()

componentDidUpdate(prevProps, prevState)

componentDidUpdate()会在更新发生后立即被调用。该方法并不会在初始化渲染时调用。

当组件被更新时,使用该方法是操作DOM的一次机会。这也是一个适合发送请求的地方,要是你对比了当前属性和之前属性(例如,如果属性没有改变那么请求也就没必要了)。

注意

shouldComponentUpdate()返回false,componentDidUpdate()将不会被调用。


卸载过程:

11.componentWillUnmount()

componentWillUnmount()

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


12.componentDidCatch()

componentDidCatch(error, info)

错误边界是React组件,并不是损坏的组件树。错误边界捕捉发生在子组件树中任意地方的JavaScript错误,打印错误日志,并且显示回退的用户界面。错误边界捕捉渲染期间、在生命周期方法中和在它们之下整棵树的构造函数中的错误。

如果定义了这一生命周期方法,一个类组件将成为一个错误边界。在错误边界中调用setState()让你捕捉当前树之下未处理的JavaScript错误,并显示回退的用户界面。只使用错误边界来恢复异常,而不要尝试将它们用于控制流。

详情请见React 16中的错误处理

注意

错误边界只捕捉树中发生在它们之下组件里的错误。一个错误边界并不能捕捉它自己内部的错误。

13.forceUpdate()

component.forceUpdate(callback)

默认情况,当你的组件或状态发生改变,你的组件将会重渲。若你的render()方法依赖其他数据,你可以通过调用forceUpdate()来告诉React组件需要重渲。

调用forceUpdate()将会导致组件的 render()方法被调用,并忽略shouldComponentUpdate()。这将会触发每一个子组件的生命周期方法,涵盖,每个子组件的shouldComponentUpdate() 方法。若当标签改变,React仅会更新DOM。

 

通常你应该尝试避免所有forceUpdate() 的用法并仅在render()函数里从this.propsthis.state读取数据。

 

 

其他:

setState()

setState(updater, [callback])

setState()将需要处理的变化塞入(译者注:setState源码中将一个需要改变的变化存放到组件的state对象中,采用队列处理)组件的state对象中, 并告诉该组件及其子组件需要用更新的状态来重新渲染。这是用于响应事件处理和服务端响应的更新用户界面的主要方式。

setState()认为是一次请求而不是一次立即执行更新组件的命令。为了更为可观的性能,React可能会推迟它,稍后会一次性更新这些组件。React不会保证在setState之后,能够立刻拿到改变的结果。

setState()不是立刻更新组件。其可能是批处理或推迟更新。这使得在调用setState()后立刻读取this.state的一个潜在陷阱。代替地,使用componentDidUpdate或一个setState回调(setState(updater, callback)),当中的每个方法都会保证在更新被应用之后触发。若你需要基于之前的状态来设置状态,阅读下面关于updater参数的介绍。

除非shouldComponentUpdate() 返回false,否则setState()永远都会导致重渲。若使用可变对象同时条件渲染逻辑无法在shouldComponentUpdate()中实现,仅当新状态不同于之前状态时调用setState(),将避免不必要的重渲。

第一个函数是带签名的updater函数:

(prevState, props) => stateChange

prevState是之前状态的引用。其不应该被直接改变。代替地,改变应该通过构建一个来自于prevState 和 props输入的新对象来表示。例如,假设我们想通过props.step在状态中增加一个值:

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});

updater函数接收到的prevState 和 props保证都是最新的。updater的输出是和prevState的浅合并。

setState()的第二个参数是一个可选地回调函数,其将会在setState执行完成同时组件被重渲之后执行。通常,对于这类逻辑,我们推荐使用componentDidUpdate

你可以选择性地传递一个对象作为 setState()的第一个参数而不是一个函数:

setState(stateChange, [callback])

其仅是将stateChange浅合并到新状态中。例如,调整购物车中物品数量:

this.setState({quantity: 2})

这一形式的setState()也是异步的,并在相同的周期中多次调用可能会被合并到一起。例如,若你在相同的周期中尝试多次增加一件物品的数量,其等价于:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

之后的调用在同一周期中将会重写之前调用的值,因此数量仅会被加一。若之后的状态依赖于之前的状态,我们推荐使用updater函数形式:

this.setState((prevState) => {
  return {counter: prevState.quantity + 1};
});

更多细节,查看State & 生命周期指南

测试代码:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  changeColor = ()=>{
    console.log('按钮:  改变状态')
    this.setState({
      color:'blue',
    })
  }
  changeIfyes = ()=>{
    console.log('按钮:  改变shouldComponentUpdate的返回值true or false');
    this.setState({
      ifyes : !this.state.ifyes,
    })
  }
  // 在装配前被调用,是初始化状态的合适位置
  constructor(props){
    super(props);
    console.log('装配之前调用constructor')
    this.state = {
      color : props.initialColor ,
      ifyes : true,
    }
  }
  // 组件实例化后和接受新属性时调用
  static getDerivedStateFromProps(nextProps,prevState){
    console.log('组件示例化or接受新属性时调用','getDerivedStateFromProps',nextProps,prevState);
    // 如果要处理变化 比较新旧值
    if(nextProps.initialColor !== prevProps.initialColor ){
      // 它应该返回一个对象来更新状态,
      return {
        initialColor:nextProps.initialColor
      }
    }
    // 或者返回null来表明新属性不需要更新任何状态。
    return null;
  }
  //
  // UNSAFE_componentWillMount(){
  //  console.log('在装配发生前被立刻调用')
  // }
  //
  render() {
    console.log('渲染中')
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        <button onClick={this.changeColor}>改变state</button>
        <button onClick={this.changeIfyes}>改变是否重新渲染</button>
      </div>
    );
  }
  // 出错
  componentDidCatch(error,info){
    console.log(error,info)
  }
  //
  componentDidMount(){
    console.log('装配后立刻被调用componentDidMount')
  }
  // 接受新属性
  // UNSAFE_componentWillReceiveProps(nextProps){
  //  console.log(nextProps)
  // }

  // 当前状态或属性的改变是否不影响组件的输出 渲染前被调用,默认值true调用render(),false不会调用render()
  shouldComponentUpdate(nextProps,nextState){
    // 可以手动比较propes和state 如果更新要忽略就返回false
    console.log('状态改变时返回true or false ','新数据',nextProps,nextState,'旧数据',this.props,this.state);
    return nextState.ifyes;
  }
  //
  forceUpdate(callback){
    console.log('准备强制渲染',callback);
  }
  //
  // UNSAFE_componentWillUpdate(nextProps,nextState){
  //  console.log(nextProps,nextState);
  // }

  // 在最新的渲染输出到DOM前立即调用
  getSnapshotBeforeUpdate(prevProps,prevState){
    console.log('取得snapshot','在最新的渲染输出到DOM前立即调用','它让你的组件能在当前的值可能要改变前获得它们',prevProps,prevState);
    // 返回的对象将作为componentDidUpdate的参数:snapshot
    return {prevProps,prevState};
  }
  //
  componentDidUpdate(prevProps,prevState,snapshot){
    console.log('更新发生后立即被调用componentDidUpdate',prevProps,prevState,snapshot);
  }
  //
  componentWillUnmount(){
    console.log('组件被卸载和销毁之前被调用')
  }

}

export default App;

ReactDOM.render(<App initialColor='red'/>, document.getElementById('root'));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值