React生命周期总结v16+和v16-

React的生命周期

生命周期就函数就是指在某一个时间刻会自动调用执行的函数,React生命周期分为三个阶段

挂载:组件被创建并插入真实dom
渲染(更新):组建的props或state发生变化时触发的组件更新
卸载:组件被从DOM中移除

生命周期有两个版本,react16.3版本引入了新版本生命周期的方法【new life cycle】,react16.3之前,react使用的都是旧版本的生命周期【old life cycle】

old life cycle

React生命周期总结v16+和v16-_第1张图片

new life cycle

React生命周期总结v16+和v16-_第2张图片

挂载(Mounting):

constructor

constructor 只会在组件初始化的时候触发一次
组件的构造函数,第一个被执行,若没有显式定义它,会有一个默认的构造函数,但是若显式定义了构造函数,我们必须在构造函数中执行
super(props),否则无法在构造函数中拿到this。如果不初始化 state 或不进行方法绑定,则不需要为 React
组件实现构造函数Constructor。 constructor中通常只做两件事:【初始化组件state,给事件处理方法绑定this】

constructor(props){
        // 要在构造函数中调用 setState,可以直接给 state 设置初始值
        super(props);
        this.state={
            count: 0,
            inputValue:"",
        },
        this.handleClick = this.handleClick.bind(this)
    }

getDerivedStateFromProps:

该函数会在组件化实例化后和重新渲染前调用(生成 VirtualDOM 之后,实际 DOM 挂载之前)
这是个静态方法,所以不能在这个函数里使用,this,有两个参数 props 和 state,分别指接收到的新参数和当前组件的 state对象,这个函数会返回一个对象用来更新当前的 ,state 对象,如果不需要更新可以返回 null。
无论是父组件的更新、props的变化或通过 setState 更新组件内部的 State,它都会被调用。
该生命周期函数必须有返回值,返回一个对象来更新 State,若返回 null 表明新 props 不更新 state。新的生命周期加了新特性,当组件实例化时,该方法替代了componentWillMount,而当接收新的 props 时,该方法替代了 componentWillReceiveProps 和componentWillUpdate。
适用于表单获取默认值。
static getDerivedStateFromProps(props, state)

import React, { Component } from 'react';  //(0)【引入组件第一种写法】
import '../App.css';
    class Homes extends Component {         //(0-1)对应第一种引入react引入方法类写法
        //(1)接下来两行为固定写法
        constructor(props) {
            // 要在构造函数中调用 setState,可以直接给 state 设置初始值
            super(props);
            this.state = {
                count: 0,
                inputValue: "",
            },
                this.handleClick = this.handleClick.bind(this)
        }
        handleClick = () => {
            this.setState({
                count: this.state.count + 1
            })
        }
        static getDerivedStateFromProps(props, state) {
            if (props.count !== state.count) {
                return {
                    count: props.count
                }
            }
            return null
        }
      render() {
        return (
          <div>
            <h1 onClick={this.handleClick}>Hello, world!{this.state.count}</h1>
          </div>
        )
      }
    };
export default Homes;

现在可以显式传入 counter ,但是这里有个问题,如果想要通过点击实现 state.counter 的增加,但这时会发现值不会发生任何变化,一直保持 props 传进来的值。这是由于在 React 16.4^ 的版本中 setState 和 forceUpdate 也会触发这个生命周期,所以当组件内部 state 变化后,就会重新走这个方法,同时会把 state 值赋值为 props 的值。因此需要多加一个字段来记录之前的 props 值,这样就会解决上述问题。

import React, { Component } from 'react';  //(0)【引入组件第一种写法】
import '../App.css';
class Homes extends Component {         //(0-1)对应第一种引入react引入方法类写法
    //(1)接下来两行为固定写法
      constructor(props) {
            // 要在构造函数中调用 setState,可以直接给 state 设置初始值
            super(props);
            this.state = {
               count: 0,
 preCount:0,
                inputValue: "",
            },
                this.handleClick = this.handleClick.bind(this)
        }
        handleClick = () => {
            this.setState({
                count: this.state.count + 1
            })
        }
        static getDerivedStateFromProps(props, preCount) {
            if (props.count !== state.preCount) {
                return {
                    count: props.count,
      preCount:props.counter
                }
            }
            return null
        }
      render() {
        return (
          <div>
            <h1 onClick={this.handleClick}>Hello, world!{this.state.count}</h1>
          </div>
        )
      }
    };
export default Homes;

render:

组件初始化,页面state或props发生变化时执行。 render是React 中最核心的方法,一个组件中必须要有这个方法,它会根据状态
state 和属性 props 渲染组件。这个函数只做一件事,就是返回需要渲染的内容,所以不要在这个函数内做其他业务逻辑,通常调用该方法会返回以下类型中一个:
       a.React元素这里包括原生的DOM 以及React 组件。
       b.数组和Fragment(片段):可以返回多个元素。
       c.Portals(插槽):可以将子元素渲染到不同的DOM中。
       d.字符串和数字:被渲染成DOM中的text节点。
       e.布尔值或者null:不渲染任何内容。

componentDidMount :

在组件挂载完成之后 (插入DOM树后) 立即调用。
componentDidMount()会在组件挂载后(插入 DOM 树中)立即调用。
该阶段通常执行依赖于DOM的操作,发送网络请求,添加订阅消息(会在componentWillUnmount取消订阅),添加事件监听,如果使用了Redux之类的数据管理工具也能触发action处理数据变化逻辑。此钩子函数中允许使用setState改变组件内的State。
注意:该生命周期函数在进行服务器端渲染时候不会触发(仅客户端有效)。如果在componentDidMount中调用setState,就会触发一次额外的渲染,多调用了一次render函数,由于他是在浏览器刷新屏幕前执行的,所以用户对此没有感知的,但是应当避免这样使用,这样带来的一定的性能问题,精良在constructor中初始化state对象。

class App extends React.Component  {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }
  componentDidMount () {
    this.setState({
      count: 1
    })
  }
  render ()  {
    return (
      <div className="count">
        counter值: { this.state.count }
      </div>
    )
  }
}

更新(Updation):

setState、useState、props变化、forceUpdate会触发组件的更新,但是组件是否从新渲染取决于shouldComponentUpdate。

shouldComponentUpdate:

函数会在组件更新之前,自动被执行。它要求返回一个布尔类型的结果,必须有返回值。返回true时组件更新,返回false则不更新。
当props或者state发生变化,在重新渲染执行之前,第一个是即将更新的props值,第二个是即将更新后的state值,可以根据更新前后的props或者state来比较加一些限制条件,决定是否更新进行性能优化,当该方法返回的布尔值false告知react无需重新渲染时,render,UNSAFE_componentWillUpdate和conponentDidUpdate等生命周期钩子函数都不会被触发。
    a.  此钩子函数在初始化渲染和使用forceUpdate方法的情况下不会被触发, 使用forceUpdate会强制更新
    b.  禁止在此函数中使用setState方法,会导致循环调用

shouldComponentUpdate(){
    console.log('shouldComponentUpdate---组件发生改变前执行')
    return true
}

现在就可以在控制台,console里看到结果了,并且结果是每次文本框发生改变时都会随着改变。如果你返回了
false,这组件就不会进行更新了。 简单点说,就是返回true,就同意组件更新;返回false,就反对组件更新。

getSnapshotBeforeUpdate (新):

保存状态快照。该生命周期函数会在组件即将挂载时触发,它的触发在 render 渲染函数之后。 这个方法在render之后,componentDidUpdate之前调用,有两个参数prevProps和prevState,标识更新之前的props和state,这个函数必须要和componentDidUpdate一起使用,并且要有一个返回值,默认是null,这个返回值座位第三个参数传递给componentDidUpdate。该生命周期函数执行完毕后就会立即触发componentDidUpdate 生命周期钩子。

getSnapshotBeforeUpdate(prevProps, prevState) {   
 console.log('#enter getSnapshotBeforeUpdate');   
 return 'foo';
}
componentDidUpdate(prevProps, prevState, snapshot) {   
  console.log('#enter componentDidUpdate snapshot = ', snapshot);
}

该生命周期函数能让你捕获某些从 DOM 中才能获取的(可能会变更的例如滚动位置),此生命周期返回的任何值都会作为第三个参数传递给 componentDidUpdate()。如不需要传递任何值,那么请返回 null。

componentDidUpdate:

在组件更新之后执行,它是组件更新的最后一个环。 该方法有三个参数【componentDidUpdate(prevProps,prevState, snapshot){}】
    a.  prevProps: 更新前的props
    b.  prevState: 更新前的state
    c.  snapshot: getSnapshotBeforeUpdate()生命周期的返回值
组件每次重新渲染后会触发 componentDidUpdate 但是只有在组件首次渲染(即初始化)时触发的是componentDidMount钩子函数,初始化渲染后componentDidMount 不会再被触发。 操作 DOM;发送网络请求。 在componentDidUpdate 生命周期函数中调用 setState 方法时,确实需要加上条件判断,以避免死循环的发生。通过判断 prevProps、prevState 和 this.state 之间的数据变化,来判断是否执行相关的 state 变更逻辑,这使得尽管在 componentDidUpdate 中调用了 setState 进行再更新,如果条件不成立state就不会进行更新,从而避免了死循环的发生。

	componentDidUpdate(prevProps){
		if (this.props.id !== prevProps.id){
			this.fetchData(this.props.id);
		}
	}

卸载(Unmounting):

componentWillUnmount:

它是在组件去除时执行。在组件卸载和销毁之前触发。可以利用这个生命周期方法进行事件的解绑。 用于移除事件监听器;取消网络请求;取消定时器;解绑DOM 事件。 在该方法中调用 setState 不会触发 render,因为所有的更新队列,更新状态都被重置为null。这个生命周期在一个组件被卸载和销毁之前被调用,因此不应该再这个方法中使用setState,因为组件一旦被卸载,就不会再装载,也就不会重新渲染。
新版本的生命周期就开始废弃了componentWillMount、componentWillReceiveProps、componentWillUpdate的三个钩子,并为这几个钩子提供了别名

  • UNSAFE_componentWillMount:当组件被渲染出来之前
  • UNSAFE_componentWillReceiveProps:当组件收到新的props之前
  • UNSAFE_componentWillUpdate:组件更新前

父子组件加载顺序

父子组件初次加载会在子组件挂挂载完成后继续父组件的挂载

  • Parent 组件: constructor()
  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: render()
  • Child 组件: constructor()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: render()
  • Child 组件: componentDidMount()
  • Parent 组件: componentDidMount()

当执行render子组件的时候,才会进入子组件的生命周期,子组件的周期结束后,再回到上级的周期。 改变子组件自身状态

Child 组件: getDerivedStateFromProps()

  • Child 组件: shouldComponentUpdate()
  • Child 组件: render()
  • Child 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidUpdate()

修改父组件传入给子组件的 props 当父组件修改传递的 props 值时,会触发父子组件的更新

Parent 组件: getDerivedStateFromProps()

  • Parent 组件: shouldComponentUpdate()
  • Parent 组件: render()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: shouldComponentUpdate()
  • Child 组件: render()
  • Child 组件: getSnapshotBeforeUpdate()
  • Parent 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidUpdate()
  • Parent 组件: componentDidUpdate()

只修改父组件的值 当父组件的值被修改时,会触发父组件的 render() 方法重新渲染,并递归地 r-render 子组件。这个过程中,会依次触发所有子组件的生命周期方法:

  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: shouldComponentUpdate()
  • Parent 组件: render()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: shouldComponentUpdate()
  • Child 组件: render()
  • Child 组件: getSnapshotBeforeUpdate()
  • Parent 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidUpdate()
  • Parent 组件: componentDidUpdate()

所以不管父组件有没有把数据传递给子组件,只要父组件
setState,都会走一遍子组件的更新周期。而且子组件被动更新会比主动更新所执行的流程多出来一个
componentWillReceiveProps 方法。

子组件修改自身state

当我们修改子组件中的数据时,只有子组件会经历更新生命周期,而父组件不会。

  • 子组件 getDerivedStateFromProps
  • 子组件 shouldComponentUpdate
  • 子组件 render
  • 子组件 getSnapShotBeforeUpdate
  • 子组件 componentDidUpdate

卸载子组件,触发父组件的重新渲染、子组件销毁

  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: shouldComponentUpdate()
  • Parent 组件: render()
  • Parent 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentWillUnmount()
  • Parent 组件: componentDidUpdate()

当子组件自身状态改变时,不会对父组件产生副作用的情况下,父组件不会进行更新,即不会触发父组件的生命周期,当父组件中状态发生变化(包括子组件的挂载以及卸载)时,会触发自身对应的生命周期以及子组件的更新。

完结~

你可能感兴趣的:(React,react.js,javascript,前端,webpack,前端框架,ecmascript,hook)