本文转载自:众成翻译
译者: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> ) } }
使用这个新的prop
(requestRefresh
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); } } // ... }
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,以便在整个团队和应用中共享组件时使用。