4

本文转载自:众成翻译
译者:iOSDevLog
链接:http://www.zcfy.cc/article/3827
原文:https://www.fullstackreact.com/30-days-of-react/day-7/

今天,我们将看看我们可以用于React组件的一些最常见的生命周期钩子函数,我们将讨论为什么它们是有用的,什么时间应该用什么。

恭喜!我们已经在React的第一周结束了,我们已经覆盖了这么多的基础知识。我们刚刚完成了处理有状态的组件来跟踪组件的内部状态。今天,我们将暂停实施,并谈一下应用中的组件_lives_。也就是说,我们将讨论组件的生命周期。

由于React装载了我们的应用,它为我们提供了一些钩子,我们可以在组件生命周期的不同时间插入自己的功能。为了_hook into_到生命周期,我们需要在我们的组件上定义函数,每个钩子在适当的时候对其进行调用。让我们来看看第一个生命周期钩子:

componentWillMount() / componentDidMount()

当我们的应用中的一个页面上定义了一个组件时,在定义虚拟节点时,我们不能立即依赖它在DOM中可用。相反,我们必须等到组件本身在浏览器中实际上是_mounted_。对于我们需要运行的功能,我们可以定义两个不同的_hooks_(或函数)。在组件被装载在页面之前被调用的一个,在组件被装载之后被调用的一个。

什么是 mounting?

由于我们使用React定义了我们的DOM树中的节点的virtual representation,我们实际上并没有定义DOM节点。相反,我们正在建立一个内存视图,React为我们维护和管理。当我们谈论mounting时,我们谈论的是将虚拟组件转换为由React放置在DOM中的实际DOM元素的过程。

这对于诸如获取数据来填充组件的事情非常有用。例如,假设我们想使用我们的活动跟踪器来显示github事件。只有当数据本身被渲染时,我们才想加载这些事件。

回想一下我们在我们的活动列表中定义了我们的Content 组件

class Content extends React.Component {
  render() {
    const {activities} = this.props; // ES6 destructuring

    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        {activities.map((activity) => (
          <ActivityItem
            activity={activity} />
        ))}

      </div>
    )
  }
}

让我们更新Content 组件,向github.com events api发出请求,并使用响应来显示活动。 因此,我们需要更新对象的state

就像我们昨天做的那样,我们通过在构造函数中将this.state 设置为一个对象来更新我们的组件为状态

class Content extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      activities: []
    }
  }

  // ...
}

现在,当组件本身准备装载(或装载之后)时,我们将要发出一个HTTP请求。通过在我们的组件中定义函数componentWillMount() (或componentDidMount()),React将在DOM中装载该方法之前运行该方法。 这是我们添加 GET 请求的完美的地方。

让我们更新content组件,并请求github api。 由于我们只想显示一个小列表,我们来看最新的四个活动。

我们已经存储了一个github数据的静态JSON文件,我们将直接从源码(我们将在几天内使用AJAX请求)使用promises。 现在,让我们重点介绍如何使用新的数据实现更新组件:

class Content extends React.Component {
  // ...
  componentWillMount() {
    this.setState({activities: data});
  }
  // ...
}

请注意,我们没有从我们的Content 组件更改任何内容,它正常工作了。

componentWillUpdate() / componentDidUpdate()

有时我们会在更改实际呈现之前或之后更新我们组件的一些数据。 例如,假设当组件的属性更改时,我们要调用一个函数来设置渲染或调用一个函数集。 componentWillUpdate() 方法是一个合理的钩子来处理我们的组件进行更改(只要我们不调用this.setState() 来处理它,因为它会导致无限循环)。

由于我们真的不需要深入处理这个问题,所以我们不用担心在这里设置一个例子,但是很高兴知道它存在。 我们使用的一个更常见的生命周期钩子是componentWillReceiveProps() 钩子。

componentWillReceiveProps()

当组件即将接收新的props时,React会调用一个方法。 这是当组件将要接收一组新的属性时将被调用的第一种方法。 定义这种方法是寻找特定props 更新的好时机,因为它使我们有机会计算更改并更新组件的内部状态。

这时我们可以根据新属性更新我们的状态。

这里要注意的一点是,即使componentWillReceiveProps() 方法被调用,props 的值可能没有更改。 总是 检查prop值的变化是一个好主意。

例如,让我们添加一个_刷新_按钮到我们的活动列表,以便我们的用户可以请求重新请求github事件api。

我们将使用componentWillReceiveProps() 钩子来要求组件重新加载它的数据。 由于我们的组件是有状态的,我们将要使用新数据刷新此状态,因此我们不能简单地更新组件中的props 。 我们可以使用componentWillReceiveProps() 方法来_告诉_我们要刷新的组件。

我们在我们的包含元素上添加一个按钮,该元素传递一个requestRefresh布尔属性来告诉Content组件刷新。

class Container extends React.Component {
  constructor(props) {
    super(props);

    this.state = {refreshing: false}
  }

  // Bound to the refresh button
  refresh() {
    this.setState({refreshing: true})
  }

  // Callback from the `Content` component
  onComponentRefresh() {
    this.setState({refreshing: false});
  }

  render() {
    const {refreshing} = this.state;
    return (
      <div className='notificationsFrame'>
        <div className='panel'>
          <Header title="Github activity" />
          {/* refreshing is the component's state */}
          <Content
            onComponentRefresh={this.onComponentRefresh.bind(this)}
            requestRefresh={refreshing}
            fetchData={fetchEvents} />
          {/* A container for styling */}
          <Footer>
            <button onClick={this.refresh.bind(this)}>
              <i className="fa fa-refresh" />
              Refresh
            </button>
          </Footer>
        </div>
      </div>
    )
  }
}

<Footer />

请注意,我们有一个新元素显示元素的子元素。 这是一种允许我们围绕一些内容添加CSS类的模式。

class Footer extends React.Component {
  render() {
    return (
      <div className='footer'>
        {this.props.children}
      </div>
    )
  }
} 

使用这个新的proprequestRefresh prop),当我们的state对象改变值时,我们可以更新activities

class Content extends React.Component {
  // ...
  componentWillReceiveProps(nextProps) {
    // Check to see if the requestRefresh prop has changed
    if (nextProps.requestRefresh !== this.props.requestRefresh) {
      this.setState({loading: true}, this.updateData);
    }
  }
  // ...
}

此演示使用JSON文件中的静态数据,并在刷新时随机挑选四个元素。 这被设置为simulate刷新。

componentWillUnmount()

在组件卸载之前,React将调用componentWillUnmount() 回调。 这是处理我们可能需要的任何清理事件的时候,例如清除超时,清除数据,断开Websockets等。

例如,我们上次工作使用我们的时钟组件,我们设置一个超时时间被称为每秒钟。 当组件准备卸载时,我们希望确保我们清除此超时,以便我们的JavaScript不会继续为不存在的组件运行超时。

回想一下,我们构建的 timer 组件看起来像这样:

import React from 'react'

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = this.getTime();
  }

  componentDidMount() {
    this.setTimer();
  }

  setTimer() {
    this.timeout = setTimeout(this.updateClock.bind(this), 1000);
  }

  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }

  getTime() {
    const currentTime = new Date();
    return {
      hours: currentTime.getHours(),
      minutes: currentTime.getMinutes(),
      seconds: currentTime.getSeconds(),
      ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
    }
  }

  // ...
  render() {

  }
}

export default Clock

当我们的时钟将被卸载时,我们将要清除我们在组件的setTimer() 函数中创建的超时。 添加componentWillUnmount() 函数负责这个必要的清理。

class Clock extends React.Component {
  // ...
  componentWillUnmount() {
    if (this.timeout) {
      clearTimeout(this.timeout);
    }
  }
  // ...
}

这些是我们可以在React框架中进行交互的一些生命周期钩子。 当我们构建我们的应用程序时,我们将会很多地使用这些应用,所以熟悉它们的方法是一个好主意,它们是如何存在,以及如何挂钩组件的生命。

我们在这篇文章中介绍了一个新的概念,我们已经看到了:我们在一个要从子组件调用到它的父组件上添加了一个回调。 在下一节中,我们将介绍如何定义和记录组件的prop API,以便在整个团队和应用中共享组件时使用。


不想成熟的大叔
882 声望526 粉丝

为学习前端开发不再枯燥、困难和迷茫而努力。