React学习笔记 - 组件的生命周期

206 阅读3分钟

每个组件都包含生命周期方法,在运行过程中的特定的阶段主动执行。我们可以重写这些方法,以便在不同阶段做相应的操作。

组件的生命周期可分为三个阶段: 挂载阶段 / 更新阶段 / 卸载阶段。

挂载阶段

  1. constructor()
  2. static getDerivedStateFromProps(props, state)
  3. componentWillMount() (v16.3不安全的)
  4. render()
  5. componentDidMount()

1. constructor()

  • 在 React 组件挂在之前调用
  • 用于初始化 state ,进行方法绑定

2. static getDerivedStateFromProps(props, state)

  • 会在调用 render() 之前调用,并且在初始挂载及后续更新中都会调用。应返回一个对象来更新 state

3. componentWillMount()

  • v16.3不安全的,可转换,v17将不可用,如果使用了新API,再使用不安全的API会报错
  • 组件被挂载DOM前调用1次,setState() 不会引起组件渲染

4. render()

  • 组件唯一必须方法;
  • 根据组件的 props 与 state 的变化返回 React 元素,用于描述组件的UI
  • render() 应为纯函数,如需修改 state,需在其它生命周期如 componentDidMount() 中进行操作
  • 如果更新阶段的 shouldComponentUpdate() 返回 false ,则不会调用 render()

5. componentDidMount()

  • 会在组件已经被渲染到DOM之后运行,只执行一次,可在其中使用 this.setState() 使组件重新渲染,可在此向后端请求数据

更新阶段

当组件的 state 和 props 发生变化时,组件将重新渲染,或者调用 forceUpdate() 时,也会触发 render() 重新渲染,更新 UI

  1. static getDerivedStateFromProps(props, state) (同挂载阶段2)
  2. componentWillReceiveProps(nextProps) (v16.3不安全的)
  3. shouldComponentUpdate(nextProps, nextState)
  4. componentWillUpdate() (v16.3不安全的)
  5. render() (同挂载阶段4)
  6. getSnapshotBeforeUpdate(prevProps, prevState)
  7. componentDidUpdate(prevProps, prevState, snapshot)

2. componentWillReceiveProps(nextProps)

  • v16.3不安全的,可转换,v17将不可用
  • 在已挂载的组件接收新的 props 之前调用,只在更新阶段触发
  • 只 props 改变触发,state 改变不触发
  • 如果需要更新状态以响应 props 变化,通过比较 this.propsnextprops 是否相同,而后使用 this.setState() 执行 state 转换。

3. shouldComponentUpdate(nextProps)

  • 当 state 或 props 发生变化,在渲染之前调用
  • 通过返回 false 来告知 React 跳过更新
  • 若返回 false 则不会再触发 componentWillUpdaterendercomponentDidUpdate 方法

4. componentWillUpdate()

  • v16.3不安全的,可转换,v17将不可用
  • 当 state 或 props 发生变化,在渲染之前调用,只在更新阶段触发
  • 不能使用 this.setState() 方法,或其他会触发更新的方法
  • 尽量使用 componentDidUpdate() 替代此方法

6. getSnapshotBeforeUpdate(prevProps, prevState)

  • 不常用
  • 在最近一次渲染输出之前调用,在组件改变之前从 DOM 中获取一些信息并作为返回值返回
  • 返回值将传递给 componentDidUpdate() 作为第三个参数,如果无返回值则为 undefind

7. componentDidUpdate(prevProps, prevState, snapshot)

  • 组件更新后立即调用,只在更新阶段触发
  • 可操作 DOM 或进行网络请求
  • 可以使用 this.setState() 方法,但必须在 if 语句中,以避免死循环

卸载阶段

  1. componentWillUnmount()

1. componentWillUnmount()

  • 当组件从 DOM 中移除时,会调用此方法
  • 再次方法中执行必要的清理操作,如清除 timer ,取消网络请求等
  • 不应使用 this.setState() 方法,因为组件不会再进行渲染

PS:应当尽量避免使用不安全的 API,且避免共用新 API 和不安全的 API。

示例代码

包含大部分生命周期钩子函数,为避免共用新旧 API 导致的报错,已将新 API 注释。

import React, { Component } from 'react';

class LifecycleIn extends Component {
  // 挂载阶段
  constructor(props) {
    super(props);
    this.state = {date: new Date()}
    console.log('---- 挂载阶段 start ----')
    console.log('constructor');
  }

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

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

  componentDidMount() {
    // 挂载时的生命周期钩子
    console.log('componentDidMount');
    console.log('---- 挂载阶段 end ----');
  }

  // 更新阶段
  componentWillReceiveProps() {
    console.log('---- 更新阶段 start ----')
    console.log('componentWillReceiveProps');
  }

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

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

  // getSnapshotBeforeUpdate() {
  //   console.log('getSnapshotBeforeUpdate');
  //   return 'getSnapshotBeforeUpdate';
  // }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log(`componentDidUpdate - ${snapshot}`);
    console.log('---- 更新阶段 end ----');
  }

  componentWillUnmount() {
    // 组件卸载时的生命周期钩子
    console.log('---- 卸载阶段 end ----');
    console.log('componentWillUnmount');
    console.log('---- 卸载阶段 end ----');
  }

  tick = () => {
    console.log('---- 更新阶段 start ----')
    this.setState({
      date: new Date()
    });
  }

  render() {
    console.log('render');
    return (
      <div>
        <button onClick={this.tick}>修改state</button>
        <p>Hello {this.props.name}!</p>
        <p>{this.state.date.toLocaleTimeString()}</p>
      </div>
    );
  }
}

export default class Lifecycle extends Component {
  state = {
    name: 'World',
  }

  changeLifecycleName = () => {
    this.setState({
      name: this.state.name == 'React' ? 'World' : 'React',
    })
  }

  render() {
    return (
      <div>
        <button onClick={this.changeLifecycleName}>修改props</button>
        <LifecycleIn name={this.state.name} /> 
      </div>
    )
  }
}