总览
React组件封装了部分UI。 完整的React应用程序UI呈现为许多嵌套组件的树。 根据应用程序的流程,各个组件需要在渲染之前和之后以及更新之前和之后执行一些任务。
最后,清理和错误处理也很重要。 React提供了许多生命周期方法,您可以覆盖它们并将它们自己的逻辑插入正确的位置。 在本教程中,您将了解从摇篮到坟墓的React组件的生命周期,每个阶段可用的方法以及何时覆盖它们。
请注意,在本教程中,我使用现代的ES6类样式。
PopularBar
我将使用一个名为PopularBar的组件来说明所有生命周期方法及其行为。 完整的源代码可在GitLab上获得 。
流行的栏包含另外两个称为ClickCounter
组件。 每个ClickCounter
组件都包含一个带有表情符号的按钮,并显示其被添加到从其主机接收的count属性中被单击的次数。 这是ClickCounter
的render()
方法:
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>
)
}
安装
当组件由父组件或根应用程序呈现时,它们便存在。 但是在呈现组件之前,需要将其构造(仅一次)并安装到虚拟DOM中(每次将其添加到虚拟DOM中)。
事件的顺序是,首先构造组件,然后调用componentWillMount()
方法,将组件安装到虚拟DOM中,然后是componentDidMount()
叫做。 这为您提供了执行不同类型的初始化的大量机会。
建设者
组件的构造函数将在每个应用程序中调用一次(如果您在浏览器中刷新页面,则该页面将被视为新应用程序)。 这是PopularBar组件的构造函数。 除了调用super()
并登录到控制台外,它实际上什么也没做。
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组件已多次安装,则应保留其点击计数。
ComponentWillMount
在componentWillMount()
安装之前将调用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的方法实现。 该组件已经存在,因此可以访问和显示其属性(属性)。
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()
方法。 然后,调用每个ClickCounter组件的构造方法和componentWillMount()
方法,然后调用两个ClickCounter组件的componentDidMount()
。 最后,将调用PopularBar的componentDidMount()
方法。 总的来说,流程是嵌套的,其中所有子组件必须完全安装,然后才能完全包含其子组件。
更新中
一旦安装了组件,就可以渲染它。 组件或从容器中接收的道具的状态可能时不时地发生变化。 这些更改导致重新渲染,但是组件有机会得到通知,甚至可以控制是否应该进行渲染。
更新过程涉及四种方法,我将按顺序介绍它们。
ComponentWillReceiveProps
从容器接收到新的道具时,将调用componentWillReceiveProps()
方法。 您可以通过this.props
访问当前道具,并通过nextProps
参数访问下一个道具。 这是ClickCounter的componentWillReceiveProps()
方法。
componentWillReceiveProps(nextProps) {
console.log(this.props.emoji +
'=== ClickCounter will receive props. ' +
'next props: ' + nextProps.count)
}
您有机会在这里检查哪些道具已更改,并根据需要修改组件的状态。 可以在这里调用this.setState()
。
应该组件更新
shouldComponentUpdate()
是一个关键方法。 当接收到新的道具时(在componentWillReceiveProps()
调用之后),或者在其他地方修改了组件的状态后,将调用该方法。 如果您未实现此方法,则组件将每次重新渲染。
但是,如果实现它并返回“ false”,则将不呈现该组件及其子组件。 请注意,如果子组件的状态被修改,即使您总是从父组件的shouldComponentUpdate()
返回“ false”,它们也将被重新渲染。
您可以在此处访问下一个道具和下一个状态,因此您拥有决策所需的所有信息。 当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
}
ComponentWillUpdate
该componentWillUpdateMethod()
在组件后称为shouldComponentUpdate()
只有shouldComponentUpdate()
返回真。 此时,您将拥有下一个道具和下一个状态。 您无法在此处修改状态,因为它将导致shouldComponentUpdate()
再次被调用。
这是代码:
componentWillUpdate(nextProps, nextState) {
console.log(this.props.emoji +
'=== ClickCounter will update' +
' nextProps.count: ' + nextProps.count +
' nextState.clickCount: ' + nextState.clickCount)
}
ComponentDidUpdate
最后,在渲染之后,调用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,这将触发更新周期。 下次更新将由父级传递新道具引起。 为了实现这一点,我将添加一个小功能,该功能每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)
}
这是输出。 请注意,已经调用了ClickCounter willReceiveProps()
方法,并且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 prop为false,PopularBar将不会呈现其ClickCounter子组件。 App组件将渲染PopularBar并根据复选框传递show prop。
这是应用程序的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
在React 16中最近添加了componentDidCatch()
方法。该方法旨在帮助解决渲染期间以前导致晦涩的错误消息的错误。 现在,可以定义特殊的错误边界组件,该组件包装可能引发错误的任何子组件,并且仅在发生错误时才呈现错误边界组件。
结论
React组件具有明确定义的生命周期,而特殊的方法可让您插入逻辑并采取措施,以非常细粒度的级别控制状态,甚至处理错误。
在大多数情况下,这不是必需的,您可以传递道具并实现render()
方法,但是很高兴知道,在更特殊的情况下,您不会盯着黑匣子。
在过去的几年中,React越来越受欢迎。 实际上,我们在市场上有许多商品可供购买,查看,实施等。 如果您正在寻找有关React的其他资源,请随时检查 。
翻译自: https://code.tutsplus.com/tutorials/mastering-the-react-lifecycle-methods--cms-29849