了解React不同的生命周期方法对于React应用程序开发非常重要,它允许我们在需要时准确触发操作。本文将介绍React中的每个生命周期,包括它们可用的方法以及它们的使用场景。
一个react组件在应用程序中经历不同的阶段,尽管在幕后发生的事情并不明显。
- mounting(安装)
- updating(更新)
- unmounting(卸载)
- error handling(错误处理)
每个阶段都有一些方法对该阶段中的组件执行特定操作。例如,从网络获取数据时,您需要在componentDidMount()方法(该方法在安装阶段可用)中调用处理API调用的函数。
mounting安装阶段
把mounting安装看作组件生命周期的初始阶段。在装载发生之前,组件还没有存在——直到装载发生,并将组件作为文档的一部分挂起。
一旦React组件安装后,我们可以使用的方法:constructor(),render(),componentDidMount()和static getDerivedStateFromProps()。
constructor()构造函数
当直接在组件上设置状态以便将方法绑定在一起时,应使用constructor()方法。
// 一旦安装了输入组件…
constructor(props) {
// …在上面设置一些props…
super(props);
this.state = {
username: ''
};
// …然后用处理输入更改的方法绑定
this.handleInputChange = this.handleInputChange.bind(this);
}
重要的是要知道构造函数是在创建组件时调用的第一个方法。该组件尚未呈现(即将到来),但DOM知道它,我们可以在它呈现之前钩住它。因此,这不是我们调用setState()或引入任何副作用的地方,因为,该组件仍处于构建阶段!
在使用React.createRef()时,可以在构造函数中设置ref。这是合法的,因为refs用于在不使用props的情况下更改值,或者使用更新值重新呈现组件:
constructor(props) {
super(props);
this.state = {
username: ''
};
this.inputText = React.createRef();
}
render()
render()方法是将一个React元素渲染到根DOM节点中。
class App extends React.Component {
// When mounting is in progress, please render the following!
render() {
return (
<div>
<p>Hello World!</p>
</div>
)
}
}
还可以用于渲染组件数组:
class App extends React.Component {
render () {
return [
<h2>JavaScript Tools</h2>,
<Frontend />,
<Backend />
]
}
}
…甚至是组件的fragments碎片:
class App extends React.Component {
render() {
return (
<React.Fragment>
<p>Hello World!</p>
</React.Fragment>
)
}
}
我们还可以使用它来呈现dom层次结构(a la React Portal)之外的组件:
// We're creating a portal that allows the component to travel around the DOM
class Portal extends React.Component {
// First, we're creating a div element
constructor() {
super();
this.el = document.createElement("div");
}
// Once it mounts, let's append the component's children
componentDidMount = () => {
portalRoot.appendChild(this.el);
};
// If the component is removed from the DOM, then we'll remove the children, too
componentWillUnmount = () => {
portalRoot.removeChild(this.el);
};
// Ah, now we can render the component and its children where we want
render() {
const { children } = this.props;
return ReactDOM.createPortal(children, this.el);
}
}
render()还可以渲染数字和字符串......
class App extends React.Component {
render () {
return "Hello World!"
}
}
以及null布尔值:
class App extends React.Component {
render () {
return null
}
}
componentDidMount()
在安装组件(即挂钩到DOM)之后调用此方法。此处请求从API获取数据。
fetchUsers() {
fetch(`https://jsonplaceholder.typicode.com/users`)
.then(response => response.json())
.then(data =>
this.setState({
users: data,
isLoading: false,
})
)
.catch(error => this.setState({ error, isLoading: false }));
}
然后在componentDidMount()钩子中调用该方法:
componentDidMount() {
this.fetchUsers();
}
我们还可以添加事件监听器:
componentDidMount() {
el.addEventListener()
}
static getDerivedStateFromProps()
static getDerivedStateFromProps()它是在安装阶段和更新阶段之前在render()方法之前调用。它返回一个对象来更新组件的状态,或者在没有任何更新的情况下返回null值。
为了理解它是如何工作的,让我们实现一个计数器组件,它的计数器状态具有一定的值。此状态仅在maxCount的值较高时才会更新。maxCount将从父组件传递。这是父组件:
class App extends React.Component {
constructor(props) {
super(props)
this.textInput = React.createRef();
this.state = {
value: 0
}
}
handleIncrement = e => {
e.preventDefault();
this.setState({ value: this.state.value + 1 })
};
handleDecrement = e => {
e.preventDefault();
this.setState({ value: this.state.value - 1 })
};
render() {
return (
<React.Fragment>
<section className="section">
<p>Max count: { this.state.value }</p>
<button
onClick={this.handleIncrement}
class="button is-grey">+</button>
<button
onClick={this.handleDecrement}
class="button is-dark">-</button>
</section>
<section className="section">
<Counter maxCount={this.state.value} />
</section>
</React.Fragment>
)
}
}
我们有一个按钮用来增加maxCount的值,我们把它传递给Counter计数器组件。
class Counter extends React.Component {
state={
counter: 5
}
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.counter < nextProps.maxCount) {
return {
counter: nextProps.maxCount
};
}
return null;
}
render() {
return (
<div className="box">
<p>Count: {this.state.counter}</p>
</div>
)
}
}
在Counter组件中,我们检查counter是否小于maxCount。如果是,我们将counter设置为maxCount的值。否则,我们什么都不做。
updating更新阶段
更新阶段发生在组件的props或state改变时。与安装一样,更新有自己的一套可用方法,值得注意的是render()和getDerivedStateFromProps()在这个阶段都会被触发。
shouldComponentUpdate()
当组件state或props改变时,我们可以使用shouldComponentUpdate()方法来控制组件是否应该更新。此方法在渲染发生之前以及正在接收state和props时调用。默认行为是true。要在每次state或props改变时重新渲染,我们会做这样的事情:
shouldComponentUpdate(nextProps, nextState) {
return this.state.value !== nextState.value;
}
当返回false时,组件不会更新,相反,调用render()方法来显示组件。
getSnapshotBeforeUpdate()
我们可以做的一件事是在某个时刻捕获组件的state,这就是getSnapshotBeforeUpdate()设计要做的。它在render()之后,但在将任何新更改提交到DOM之前调用。返回的值作为第三个参数传递给componentDidUpdate()。
它将先前的state和props作为参数:
getSnapshotBeforeUpdate(prevProps, prevState) {
// ...
}
这种方法的用例还比较少。它是那些你可能不会经常接触的生命周期方法之一。
componentDidUpdate()
如果组件更新了,那么我们可以使用这个方法在那个时候钩住它,并传递它以前的props和state。
componentDidUpdate(prevProps, prevState) {
if (prevState.counter !== this.state.counter) {
// ...
}
}
如果你曾经使用过getSnapshotBeforeUpdate(),你也可以将返回的值作为参数传递给componentDidUpdate():
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevState.counter !== this.state.counter) {
// ....
}
}
Unmounting卸载阶段
当组件从DOM中清除并且不再可用时,就会发生卸载。这里只有一种方法: componentWillUnmount()
这是在卸载和销毁组件之前调用的。例如删除可能已添加到componentdidmount()中的事件侦听器,或者清除订阅。
// Remove event listener
componentWillUnmount() {
el.removeEventListener()
}
Error Handling错误处理阶段
getDerivedStateFromError()
我们使用getDerivedStateFromError()捕获从子组件抛出的任何错误,然后使用这些错误更新组件的状态。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return (
<h1>Oops, something went wrong :(</h1>
);
}
return this.props.children;
}
}
在本例中,当从子组件抛出错误时,ErrorBoundary组件将显示“oops,something been wrong”。更多关于此方法的信息在这里。
componentDidCatch()
虽然getDerivedStateFromError()适用于在出现副作用(如错误记录)的情况下更新组件的状态,但我们应该使用componentDidcatch(),因为它是在提交阶段调用的,此时DOM已更新。
componentDidCatch(error, info) {
// Log error to service
}
getDerivedStateFromError()和componentDidCatch()都可以在ErrorBoundary组件中使用:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
// Log error to service
}
render() {
if (this.state.hasError) {
return (
<h1>Oops, something went wrong :(</h1>
);
}
return this.props.children;
}
}