• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

掌握React生命周期方法

概述

react 组件封装了部分 UI。您的完整 React 应用程序 UI 呈现为许多嵌套组件的树。根据应用程序的流程,各个组件需要在渲染之前和之后以及更新之前和之后执行一些任务。

最后,清理和错误处理也很重要。React 提供了一系列生命周期方法,您可以在正确的位置覆盖和注入您自己的逻辑。在本教程中,您将了解 React 组件从摇篮到坟墓的生命周期、每个阶段可用的方法以及何时适合覆盖它们。

请注意,我在本教程中使用了现代 ES6 类样式。

PopularBar

我将使用一个名为 PopularBar 的组件来说明所有生命周期方法以及它们的行为方式。GitLab上提供了完整的源代码

流行的 bar 包含另外两个称为ClickCounter每个ClickCounter组件都包含一个带有表情符号的按钮,并显示它被单击的次数,并将其添加到它从其主机接收的计数属性中。下面是render()方法ClickCounter

  render() { 
    return ( 
      <span className='padded' 
            onClick={() => { 
              let clickCount = this.state.clickCount + 1 
              this.setState({clickCount: clickCount}) 
            }} 
      > 
        <button>{this. props.emoji}</button> 
        {this.getTotal() < 100 ? this.getTotal() : "99+"} 
      </span> 
    ); 
  }

PopularBar 组件呈现两个 ClickCounter 组件,带有竖起大拇指和不竖起大拇指的表情符号。请注意,如果 "show" 属性为 false,它会呈现一个空 div。当我们稍后讨论安装和卸载时,这将很重要。

  render() { 
    if (!this.props.show) { 
      return (<div />) 
    } 
    return ( 
        <div className="padded" style={this.props.style}> 
          <ClickCounter 
            emoji={thumbsup} 
            count= {this.props.upCount} 
          /> 
          <ClickCounter 
            emoji={thumbsdown} 
            count={this.props.downCount} 
          /> 
        </div> 
    ) 
  }

掌握React生命周期方法  第1张

安装

React 组件在由父组件或根应用程序呈现时存在。但是在一个组件可以被渲染之前,它需要被构造(仅一次)并安装到虚拟dom中(每次它被添加到虚拟 DOM 中)。

event s的顺序是先构造组件,然后componentWillMount()调用方法,将组件挂载到虚拟 DOM 中,然后componentDidMount() 调用 。这给了你很多机会来执行不同类型的初始化。 

构造函数

每个应用程序将调用一次组件的构造函数(如果您在浏览器中刷新页面,则它被视为一个新应用程序)。这是 PopularBar 组件的构造函数。super()除了 call (这是必需的)并登录到控制台之外,它实际上并没有做任何事情 。

class PopularBar extends Component {
  constructor() {
    super()
    console.log('--- PopularBar constructor is here!')
  }

ClickCounter 的构造函数将其clickCount状态初始化为零:

class ClickCounter extends Component {
  constructor(props) {
    super(props)
    this.state = {
      clickCount: 0
    }

    console.log(props.emoji + 
                '=== ClickCounter constructor is here!')
  }

这是每个应用程序必须执行一次初始化的完美示例。如果 ClickCounter 组件被安装多次,它应该保留其点击计数。

组件WillMount

componentWillMount()方法在组件挂载之前调用,因此还没有组件。一般来说,在这个阶段没有太多可以做的事情,除非你有一些特殊的初始化,每次安装组件时都会发生,但即使这样也可以等待componentDidMount() 方法。 

以下是 PopularBar 和 ClickCounter 的方法实现:

  // PopularBar 
  componentWillMount() {
    console.log('--- PopularBar will mount. Yay!')
  }


  // ClickCounter
  componentWillMount() {
    console.log(this.props.emoji + 
                '=== ClickCounter will mount. Yay!')
  }

this.setState()如果你愿意,你可以在这里打电话。这些道具显然无法访问。

ComponentDidMount

在这里,组件已经挂载,您可以在虚拟 DOM 的上下文中执行任何需要访问组件的工作。以下是 PopularBar 和 ClickCounter 的方法实现。组件已经存在,所以它的属性(props)可以被访问和显示。

  componentDidMount() {
    console.log('--- PopularBar did mount. upCount: ' + 
                 this.props.upCount + ', downCount: ' +
                 this.props.downCount)
  }


  // ClickCounter
  componentDidMount() {
    console.log(this.props.emoji + 
                '=== ClickCounter did mount. count: ' +      
                this.props.count)
  }

总结安装部分,让我们看看 PopularBar 和它包含的两个 ClickCounter 组件中的事件顺序。为方便起见,我为每个 ClickCounter 显示了表情符号,以便区分它们。 

--- PopularBar constructor is here!
--- PopularBar will mount. Yay!
=== ClickCounter constructor is here!
=== ClickCounter will mount. Yay!
=== ClickCounter constructor is here!
=== ClickCounter will mount. Yay!
=== ClickCounter did mount. count: 5
=== ClickCounter did mount. count: 8
--- PopularBar did mount. upCount: 5, downCount: 8

首先,构建 PopularBar 并componentWillMount()调用它的方法。然后,componentWillMount()调用每个 ClickCounter 组件的构造函数和方法,然后componentDidMount()调用两个 ClickCounter 组件。最后,componentDidMount()调用了 PopularBar 的方法。总体而言,流程是嵌套的,其中所有子组件必须在其包含的组件完全安装之前完全安装。

更新

一旦组件被安装,它就可以被渲染。组件的状态或它从容器接收的 props 时不时地可能会发生变化。此类更改会导致重新渲染,但组件有机会得到通知,甚至可以控制是否应该进行渲染。 

更新过程涉及四种方法,我将按顺序介绍它们。

ComponentWillReceiveProps

componentWillReceiveProps()当从容器接收到新的道具时调用该方法。您可以通过参数访问当前的道具并通过参数访问this.props下一个道具这是ClickCounter的方法。nextPropscomponentWillReceiveProps()

  componentWillReceiveProps(nextProps) {
    console.log(this.props.emoji + 
                '=== ClickCounter will receive props. ' + 
                'next props: ' + nextProps.count)
  }

您有机会在这里检查哪些道具已更改并根据需要修改组件的状态。可以在这里打电话this.setState()

应该组件更新

shouldComponentUpdate()是一个关键的方法当接收到新的 props(componentWillReceiveProps()调用后)或在其他地方修改组件的状态后调用它。如果你不实现这个方法,那么组件每次都会重新渲染。

但是如果你实现它并返回'false',那么组件及其子组件将不会被渲染。请注意,如果子组件的状态被修改,即使您总是从父组件返回 'false',它们也会重新渲染shouldComponentUpdate()

您可以在这里访问下一个道具和下一个状态,因此您拥有做出决定所需的所有信息。ClickCounter 组件在计数超过 99 时显示 99+,因此只有在计数小于 100 时才需要更新。代码如下:

  shouldComponentUpdate(nextProps, nextState) {
    let currTotal = this.getTotal()
    let shouldUpdate = currTotal < 100

    console.log(this.props.emoji + '=== ClickCounter should ' +
                (shouldUpdate ? '' : 'NOT ') + 'update')
    return shouldUpdate
  }

组件意志更新

仅当返回true时才会componentWillUpdateMethod()在组件之后调用。在这一点上,你有下一个道具和下一个状态。你不能在这里修改状态,因为它会导致再次被调用。shouldComponentUpdate()shouldComponentUpdate()shouldComponentUpdate()

这是代码:

  componentWillUpdate(nextProps, nextState) {
    console.log(this.props.emoji + 
                '=== ClickCounter will update' +
                ' nextProps.count: ' + nextProps.count +
                ' nextState.clickCount: ' + nextState.clickCount)
  }

组件更新

最后,在渲染之后,componentDidUpdate()调用该方法。在这里调用是可以 this.setState() 的,因为之前状态变化的渲染已经完成了。

这是代码:

  componentDidUpdate() {
    console.log(this.props.emoji + '=== ClickCounter did update')
  }

让我们看看实际的更新方法。我将导致两种类型的更新。首先,我将单击竖起大拇指按钮来触发状态更改:

 --- PopularBar constructor is here!  PopularBar.js:10
--- PopularBar will mount. Yay!  PopularBar.js:14
=== ClickCounter constructor is here!
=== ClickCounter will mount. Yay!
=== ClickCounter constructor is here!
=== ClickCounter will mount. Yay!
=== ClickCounter did mount. count: 5  ClickCounter.js:20
=== ClickCounter did mount. count: 8  ClickCounter.js:20
--- PopularBar did mount. upCount: 5, downCount: 8 
=== ClickCounter should update
=== ClickCounter will update nextProps.count: 5 
                               nextState.clickCount: 1  
=== ClickCounter did update

注意nextState.clickCount是 1,它会触发更新周期。下一次更新将由 parent 传递新的 props 引起。为方便起见,我将添加一个小函数,该函数每 5 秒触发一次并将计数递增 20。这是包含 PopularBar 的主 App 组件中的代码。更改将一直传播到 ClickCounter。

class App extends Component {
  constructor() {
    super()
    this.state = {
      showPopularBar: true,
      upCount: 5,
      downCount: 8
    }
  }

  componentDidMount() {
    this.timer = setInterval(this.everyFiveSeconds.bind(this), 
                             5000);
  }

  everyFiveSeconds() {
    let state = this.state
    state.upCount += 20
    this.setState(state)
  }

这是输出。请注意,ClickCounterwillReceiveProps()方法已被调用,并且nextState.clickCount仍然为零,但nextProps.Count现在为 25。

--- PopularBar constructor is here!
--- PopularBar will mount. Yay!
=== ClickCounter constructor is here!
=== ClickCounter will mount. Yay!
=== ClickCounter constructor is here!
=== ClickCounter will mount. Yay!
=== ClickCounter did mount. count: 5
=== ClickCounter did mount. count: 8
--- PopularBar did mount. upCount: 5, downCount: 8
=== ClickCounter will receive props. next props:25
=== ClickCounter should update
=== ClickCounter will update nextProps.count: 25 
                               nextState.clickCount: 0

卸载和错误处理

组件可以卸载和重新安装,并且在组件的生命周期中可能会出现错误。

组件将卸载

如果一个组件没有被它的容器渲染,它会从虚拟 DOM 中卸载并且被卸载的组件的 componentWillUnmount()方法被调用。如果 show 属性为 false,PopularBar 将不会呈现其 ClickCounter 子组件。App 组件渲染 PopularBar 并根据复选框传递 show 属性。

这是App的render()方法:

 render() {
    return (
      <div>
        <h1>Popular Bar</h1>
        <label>
          <input
            type='checkbox'
            defaultChecked={this.state.showPopularBar}
            ref='showPopularBar'
            onChange={() => this.setState(
                 {showPopularBar: !this.state.showPopularBar})
            }
          />
          Show popular bar
        </label>

        <PopularBar
            show={this.state.showPopularBar}
            upCount={this.state.upCount}
            downCount={this.state.downCount}
        />
      </div>
    )
  }

当用户取消选中该复选框时,PopularBar 仍会呈现,但不会呈现其子组件,这些子组件将被卸载。这是代码和输出:

  componentWillUnmount() {
    console.log(this.props.emoji + 
                '=== ClickCounter will unmount :-(')
  }


Output:

=== ClickCounter will unmount :-(  
=== ClickCounter will unmount :-(

没有componentDidUnmount()方法,因为此时没有组件。

ComponentDidCatch

componentDidCatch()方法是最近在 React 16 中添加的。它旨在帮助解决渲染期间的错误,该错误以前会导致模糊的错误消息。现在,可以定义特殊的错误边界组件来包装任何可能抛出错误的子组件,并且只有在发生错误时才会呈现错误边界组件。

结论

React 组件有一个定义良好的生命周期,特殊的方法可以让你插入你的逻辑并采取行动,在非常细粒度的级别控制状态,甚至处理错误。



文章目录
  • 概述
  • PopularBar
  • 安装
    • 构造函数
    • 组件WillMount
    • ComponentDidMount
  • 更新
    • ComponentWillReceiveProps
    • 应该组件更新
    • 组件意志更新
    • 组件更新
  • 卸载和错误处理
    • 组件将卸载
    • ComponentDidCatch
  • 结论