《五》组件的生命周期

组件从创建到销毁的过程,被称为组件的生命周期。

在没有 Hooks 之前,函数组件没有生命周期,因为生命周期函数是 React.Component 类的方法实现的,函数组件没有继承 React.Component,所以也就没有生命周期。

在生命周期的整个过程,分成三个阶段:

  1. 挂载阶段(Mount):组件第一次在 DOM 树中被渲染的过程。
  2. 更新阶段(Update):组件状态发生变化,重新渲染的过程。
  3. 卸载阶段(Unmount):组件从 DOM 树中被移除的过程。

在生命周期的各个阶段都有相对应的钩子函数,会在特定的时机被调用,被称为组件的生命周期函数。

生命周期函数 = 生命周期钩子 = 生命周期回调函数 = 生命周期钩子函数

组件的生命周期函数(新):

请添加图片描述
请添加图片描述

新的生命周期钩子增加了 getDerivedStateFromProps 和 getSnapshotBeforeUpdate。

  1. constructor():在组件挂载之前被调用。

    不能在 constructor() 构造方法里调用 setState(), 因为此时第一次 render() 还未执行,也就意味 DOM 节点还未挂载。

  2. render():是类组件中唯一必须实现的方法,用于渲染 DOM。

  3. componentDidMount():在组件挂载后 (插入 DOM 树后) 被调用,此生命周期是发送网络请求、开启定时器、订阅消息、操作 DOM 等的好时机。

    可以在此钩子函数里调用 setState()

  4. componentDidUpdate(prevProps, prevState, snapshot):在组件更新后被调用。首次渲染不会执行。
    componentDidUpdate() 的第一个参数是 上一次的 props 值,第二个参数是 上一次的 state 值,第三个参数是 getSnapshotBeforeUpdate() 的返回值。

    可以比较前后 props 来进行 setState() , 否则总是 setState()会导致死循环。

  5. componentWillUnmount():在组件卸载及销毁之前被调用。此生命周期是取消网络请求、清理定时器、取消订阅等操作的好时机。

  6. static getDerivedStateFromProps(nextProps, prevState):在每次调用 render() 方法之前都会被调用,在初始化和更新时都会被调用。
    getDerivedStateFromProps() 的第一个参数为即将更新的 props,第二个参数为上一个状态的 state,可以比较 props 和 state 来加一些限制条件,防止无用的 state 更新。
    getDerivedStateFromProps() 的返回值是必须的。返回一个对象来更新 state,如果不需要更新,返回 null 即可。

    getDerivedStateFromProps() 是用于根据属性的变化计算并返回新的状态的生命周期方法,适用于 state 的值在任何时候都取决于 props 的情况。它被设计为一个纯粹的函数,用于派生状态,最好不要在其中执行副作用操作

    // 之前使用 componentWillReceiveProps
    componentWillReceiveProps(nextProps) {
        if (nextProps.location.search !== this.props.location.search) {
        	this.init()
        }
    }
    
    // 现在使用 getDerivedStateFromProps:相当于把 componentWillReceiveProps 拆分成 getDerivedStateFromProps 和 componentDidUpdate
    static getDerivedStateFromProps(nextProps, prevState) {
      const {search} = nextProps.location
      if (search !== prevState.search) {
        return {
          search,
        }
      }
      return null
    }
    componentDidUpdate(prevProps, prevState) {
      const {search} = this.state
       if (search !== prevState.search) {
        this.init()
      }
    }
    
  7. shouldComponentUpdate(nextProps, nextState):在组件更新之前被调用,可以控制组件是否进行更新, 返回 true 时组件更新, 返回 false 则不更新。不写此生命周期钩子时默认为 true。
    shouldComponentUpdate() 的第一个参数是即将更新的 props 值,第二个参数是即将更新后的 state 值。可以根据更新前后的 props 或 state 来比较加一些限制条件,决定是否更新,进行性能优化。

    不要在 shouldComponentUpdate 中调用 setState(),否则会导致无限循环调用更新、渲染,直至浏览器内存崩溃。

  8. getSnapshotBeforeUpdate(prevProps, prevState):在最新的渲染数据提交给 DOM 前会调用,也就是说,在 render() 之后,在 componentDidUpdate() 之前调用。使得组件可以在更新之前获取快照值。

    它可以使组件在 DOM 真正更新之前捕获一些信息(例如滚动位置等),此生命周期返回的任何值都会作为参数传递给 componentDidUpdate(),返回值是必须的,如不需要传递任何值,那么返回 null。

组件的生命周期流程(新):

// Home.jsx
import React, {Component} from 'react'

class Home extends Component {
  state = {
    message: 'Hello Home',
  }

  constructor(props) {
    super(props)
    console.log('Parent constructor')
  }

  componentDidMount() {
    console.log('Parent componentDidMount')
  }

  shouldComponentUpdate() {
    console.log('Parent shouldComponentUpdate')
    return true
  }

  componentDidUpdate() {
    console.log('Parent componentDidUpdate')
  }
  
  static getDerivedStateFromProps() {
    console.log('Parent getDerivedStateFromProps')
	 return null
  }

  getSnapshotBeforeUpdate() {
    console.log('Parent getSnapshotBeforeUpdate')
   }

  handleClick = () => {
    this.setState({message: 'Hello React'})
  }

  render() {
    console.log('Parent render')
    
    return (
      <div>
        <h1 onClick={this.handleClick}>{this.state.message}</h1>
      </div>
    )
  }
}

export default Home

组件的挂载流程:

请添加图片描述

组件的更新流程:

请添加图片描述

组件的卸载流程:

  1. componentWillUnmount

父子组件的生命周期流程(新):

当父子组件的生命周期函数都会被触发时,会先触发父组件 render()render() 之前的生命周期函数;然后触发子组件 render()render() 之前的的生命周围函数;然后交替触发子父组件 render() 之后的生命周期函数,总是子组件的先被触发。

Home.jsx
import React, {Component} from 'react'
import Utils from './Utils.jsx'

class Home extends Component {
  state = {
    homeMessage: 'Hello Home',
  }

  constructor(props) {
    super(props)
    console.log('Parent constructor')
  }

  componentDidMount() {
    console.log('Parent componentDidMount')
  }

  shouldComponentUpdate() {
    console.log('Parent shouldComponentUpdate')
    return true
  }

  componentDidUpdate() {
    console.log('Parent componentDidUpdate')
  }

  static getDerivedStateFromProps() {
    console.log('Parent getDerivedStateFromProps')
	  return null
	}

  getSnapshotBeforeUpdate() {
    console.log('Parent getSnapshotBeforeUpdate')
  }

  handleClick = () => {
    this.setState({homeMessage: 'Hello React'})
  }

  handleChange = (val) => {
    this.setState({homeMessage: val})
  }

  render() {
    const {homeMessage} = this.state

    console.log('Parent render')

    return (
      <div>
        <h1 onClick={this.handleClick}>{homeMessage}</h1>
        <Utils homeMessage={homeMessage} onChange={this.handleChange} />
      </div>
    )
  }
}

export default Home
Utils.jsx
import React, {Component} from 'react'

class Utils extends Component {
  state = {
    utilsMessage: 'Hello Utils',
  }

  constructor(props) {
    super(props)
    console.log('Child constructor')
  }

  componentDidMount() {
    console.log('Child componentDidMount')
  }

  shouldComponentUpdate() {
    console.log('Child shouldComponentUpdate')
    return true
  }

  componentDidUpdate() {
    console.log('Child componentDidUpdate')
  }

  static getDerivedStateFromProps() {
    console.log('Child getDerivedStateFromProps')
	  return null
	}

  getSnapshotBeforeUpdate() {
    console.log('Child getSnapshotBeforeUpdate')
  }

  handleClick = () => {
    this.setState({utilsMessage: 'Hello JS'})
  }

  hanldeMouseEnter = () => {
    this.setState({utilsMessage: 'Hello Web'})
    this.props.onChange('Hello Web')
  }

  render() {
    console.log('Child render')

    return (
      <div>
        <h2 onClick={this.handleClick} onMouseLeave={this.hanldeMouseEnter}>{this.state.utilsMessage}</h2>
        <h2>{this.props.homeMessage}</h2>
      </div>
    )
  }
}

export default Utils

父子组件的挂载流程:

请添加图片描述

修改父组件时父子组件的更新流程:

当父组件状态变化时,不会是否有传递给子组件 props 或者传递给子组件的 props 是否有变化,都会会触发自身和子组件对应的生命周期。

除非使用 React.PureComponent 或者 React.memo(),那样的话,如果没有传递给子组件 props 或者传递给子组件的 props 没有变化,除了子组件的 getDerivedStateFromProps() 生命周期函数会被触发外,子组件其他的生命周期函数将不会被触发。

请添加图片描述

修改子组件不影响父组件时的更新流程:

当子组件自身的 state 状态改变,不会对父组件产生副作用的情况下,父组件不会进行更新,也就是不会触发父组件的生命周期。
请添加图片描述

修改子组件影响父组件时的更新流程:

当子组件自身的 state 状态改变,对父组件产生副作用的情况下,父组件会进行更新,也就是会触发父组件的生命周期。
请添加图片描述

组件的生命周期函数(旧):

React 从 v16.3 开始废弃 componentWillMount、componentWillReceiveProps、componentWillUpdate 三个钩子函数。在新版本中使用需要加上 UNSAFE_ 前缀,否则会触发控制台的警告。

UNSAFE 不是指安全性,而是表示使用这些生命周期的代码在 React 的未来版本中更有可能出现 Bug,尤其是在启用异步渲染之后。

在这里插入图片描述

  1. componentWillMount:组件挂载之前调用。
  2. componentWillReceiveProps:子组件接收到父组件新的 props 之前调用。

    子组件第一次接收到父组件的 props 时不会调用。
    只要父组件重新渲染了,无论什么情况下,子组件的 componentWillReceiveProps 就会被触发。

    componentWillReceiveProps(nextProps) {
    	// 可以和 this.props 中的数据进行对比,以决定是否要执行某些方法
    }
    
  3. componentWillUpdate:组件更新之前调用。

组件的生命周期流程(旧):

// Home.jsx
import React, {Component} from 'react'

class Home extends Component {
  state = {
    message: 'Hello Home',
  }

  constructor(props) {
    super(props)
    console.log('Parent constructor')
  }

  componentWillMount() {
    console.log('Parent componentWillMount')
  }

  componentDidMount() {
    console.log('Parent componentDidMount')
  }

  shouldComponentUpdate() {
    console.log('Parent shouldComponentUpdate')
    return true
  }

  componentWillUpdate() {
    console.log('Parent componentWillUpdate')
  }

  componentDidUpdate() {
    console.log('Parent componentDidUpdate')
  }

  handleClick = () => {
    this.setState({message: 'Hello React'})
  }

  render() {
    console.log('Parent render')
    
    return (
      <div>
        <h1 onClick={this.handleClick}>{this.state.message}</h1>
      </div>
    )
  }
}

export default Home

组件的挂载流程:

请添加图片描述

组件的更新流程:

请添加图片描述

组件的卸载流程:

  1. componentWillUnmount

父子组件的生命周期流程(旧):

当父子组件的生命周期函数都会被触发时,会先触发父组件 render()render() 之前的生命周期函数;然后触发子组件 render()render() 之前的的生命周围函数;然后交替触发子父组件 render() 之后的生命周期函数,总是子组件的先被触发。

// Home.jsx
import React, {Component} from 'react'
import Utils from './Utils.jsx'

class Home extends Component {
  state = {
    homeMessage: 'Hello Home',
  }

  constructor(props) {
    super(props)
    console.log('Parent constructor')
  }

  componentWillMount() {
    console.log('Parent componentWillMount')
  }

  componentDidMount() {
    console.log('Parent componentDidMount')
  }

  shouldComponentUpdate() {
    console.log('Parent shouldComponentUpdate')
    return true
  }

  componentWillUpdate() {
    console.log('Parent componentWillUpdate')
  }

  componentDidUpdate() {
    console.log('Parent componentDidUpdate')
  }

  handleClick = () => {
    this.setState({homeMessage: 'Hello React'})
  }

  handleChange = (val) => {
    this.setState({homeMessage: val})
  }

  render() {
    const {homeMessage} = this.state

    console.log('Parent render')

    return (
      <div>
        <h1 onClick={this.handleClick}>{homeMessage}</h1>
        <Utils homeMessage={homeMessage} onChange={this.handleChange} />
      </div>
    )
  }
}

export default Home
// Utils.jsx
import React, {Component} from 'react'

class Utils extends Component {
  state = {
    utilsMessage: 'Hello Utils',
  }

  constructor(props) {
    super(props)
    console.log('Child constructor')
  }

  componentWillMount() {
    console.log('Child componentWillMount')
  }

  componentDidMount() {
    console.log('Child componentDidMount')
  }

  shouldComponentUpdate() {
    console.log('Child shouldComponentUpdate')
    return true
  }

  componentWillUpdate() {
    console.log('Child componentWillUpdate')
  }

  componentDidUpdate() {
    console.log('Child componentDidUpdate')
  }

  componentWillReceiveProps() {
    console.log('Child componentWillReceiveProps')
  }

  handleClick = () => {
    this.setState({utilsMessage: 'Hello JS'})
  }

  hanldeMouseLeave = () => {
    this.setState({utilsMessage: 'Hello Web'})
    this.props.onChange('Hello Web')
  }

  render() {
    console.log('Child render')

    return (
      <div>
        <h2 onClick={this.handleClick} onMouseLeave={this.hanldeMouseLeave}>{this.state.utilsMessage}</h2>
        <h2>{this.props.homeMessage}</h2>
      </div>
    )
  }
}

export default Utils

父子组件的挂载流程:

请添加图片描述

修改父组件时父子组件的更新流程:

当父组件状态变化时,不会是否有传递给子组件 props 或者传递给子组件的 props 是否有变化,都会会触发自身和子组件对应的生命周期。

除非使用 React.PureComponent 或者 React.memo(),那样的话,如果没有传递给子组件 props 或者传递给子组件的 props 没有变化,除了子组件的 componentWillReceiveProps() 生命周期函数会被触发外,子组件其他的生命周期函数将不会被触发。

请添加图片描述

修改子组件不影响父组件时的更新流程:

当子组件自身的 state 状态改变,不会对父组件产生副作用的情况下,父组件不会进行更新,也就是不会触发父组件的生命周期。

请添加图片描述

修改子组件影响父组件时的更新流程:

当子组件自身的 state 状态改变,对父组件产生副作用的情况下,父组件会进行更新,也就是会触发父组件的生命周期。
请添加图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值