React教程: 第7天 生命周期钩子函数

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

原著PDF版本下载:下载超过300页的 PDF

注意:这篇文章是关于经典的React生命周期挂钩。
如果您想了解新的生命周期 API,请单击此处

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

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

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

componentWillMount() / componentDidMount()

当在应用程序的页面上定义组件时,我们不能依赖它在DOM中的可用性,因为我们正在定义虚拟节点。相反,我们必须等到组件本身真正安装到浏览器中。对于安装后需要运行的功能,我们可以定义两个不同的钩子(或函数)。一个是在组件即将装入页面之前调用的,另一个是在组件装入之后调用的。

What does mounting mean?


因为我们用React定义DOM树中节点的虚拟表示,所以实际上我们并没有定义DOM节点。相反,我们正在建立一个内存视图,React为我们维护和管理。当我们讨论挂载时,我们讨论的是将虚拟组件转换为实际的DOM元素的过程,这些元素通过React放置在DOM中。

这对于获取数据以填充组件之类的事情很有用。例如,假设我们想使用活动跟踪器来显示github事件。我们只想在数据本身将被呈现时加载这些事件。

回想一下,我们在活动列表中定义了内容组件:

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});
  }
  // ...
}

我们还要稍微更新 ActivityItem 组件,以反映新的 activity 对象结构。我们也在用 Moment.js 库将日期格式化为人类友好的字符串,例如 30 min ago 要将其包含在文件中,请将以下 script 标记添加到文档中

<script src="https://unpkg.com/moment@2.24.0/min/moment.min.js"></script>
class ActivityItem extends React.Component {
  render() {
    const { activity } = this.props;

    return (
      <div className='item'>
        <div className={'avatar'}>
          <img
            alt='avatar'
            src={activity.actor.avatar_url} />
        </div>

        <span className={'time'}>
          {moment(activity.created_at).fromNow()}
        </span>
        
        <p>{activity.actor.display_login} {activity.payload.action}</p>
        <div className={'right'}>
          {activity.repo.name}
        </div>
      </div>
    )
  }
}

请注意,我们没有从我们的 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 {
  constructor {
    this.state = {
      activities: [],
      loading: false // <~ set loading to false
    };
  }
  // ...  
  updateData() {
    this.setState(
      {
        loading: false,
        activities: data.sort(() => 0.5 - Math.random()).slice(0, 4)
      },
      this.props.onComponentRefresh
    );
  }
  
  componentWillReceiveProps(nextProps) {
    // Check to see if the requestRefresh prop has changed
    if (nextProps.requestRefresh === true) {
      this.setState({ loading: true }, this.updateData);
    }
  }
  // ...
}

我们还要更新componentWillMount方法来调用此this.updateData()而不是this.setState

 class Content extends React.Component {
    // ...
    componentDidMount() {
      this.updateData();
    }
    // ...
  }

在这里插入图片描述
这个演示使用来自JSON文件的静态数据,并在刷新时随机选择四个元素。这是为了模拟刷新而设置的。

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,以便在整个团队和应用中共享组件时使用。


上一章:状态
下一章:属性类型


本教程系列的完整源代码可以在 GitHub repo, 上找到,其中包含所有样式和代码示例。
如果在任何时候你感到困扰,有进一步的问题,请随时通过以下方式与我们联系:
在原文文章末尾评论这篇文章
发送电子邮件至 [email protected]
加入我们的 gitter room
发推文给我们 @fullstackreact
REACT.JS DOM 应用 移动 JAVASCRIPT
版权声明 本译文仅用于学习、研究和交流目的,欢迎非商业转载。转载请注明出处、译者和众成翻译的完整链接。
原文链接:全栈React: React 30天


如果有疑问可以直接留言评论,如果觉得对你有帮助,可以小小的赞赏一杯奶茶钱,谢谢!!

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值