1 React生命周期
只有 class 组件才有生命周期,因为 class 组件会创建对应的实例,而函数组件不会。组件实例从被创建到被销毁的过程称为组件的生命周期。
组件的生命周期可分成三个状态:
-
**Mounting(挂载):**第一次把组件渲染到DOM树的过程
-
**Updating(更新):**当组件重新渲染
-
**Unmounting(卸载):**从DOM树中删除
图片来自菜鸟教程
1.1 Mounting过程
constructor()
: 在 React 组件挂载之前,会调用它的构造函数。getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。render()
: render() 方法是 class 组件中唯一必须实现的方法。因为React组件的父类React.Component类对除render之外的生命周期函数都有默认实现。它不做实际的渲染只是返回一个JSX描述的结构,实际的渲染是由React来操作渲染的过程。componentWillMount()
: 这个 函数会在render函数之前被调用。(既可以在浏览器端运行,也可以在服务端运行)componentDidMount()
: 在组件挂载后(插入 DOM 树中)立即调用。(只在浏览器端运行)
1.2 Mouting过程
componentWillReceiveProps在React新的生命周期中被取消,这个生命周期函数是为了替代componentWillReceiveProps,所以在需要使用componentWillReceiveProps的时候,就可以考虑使用getDerivedStateFromProps来进行替代了。所以这里引用菜鸟教程上的介绍。
每当组件的 state 或 props 发生变化时,组件就会更新。
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
-
getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。也就是当props数据某个值发生变化时对state进行赋值。 -
shouldComponentUpdate()
:当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。当 props 或 state 发生变化时,
shouldComponentUpdate()
会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用forceUpdate()
时不会调用该方法。此方法仅作为**性能优化的方式而存在。不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug。你应该考虑使用内置的
PureComponent
组件**,而不是手动编写shouldComponentUpdate()
。PureComponent
会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。如果你一定要手动编写此函数,可以将
this.props
与nextProps
以及this.state
与nextState
进行比较,并返回false
以告知 React 可以跳过更新。请注意,返回false
并不会阻止子组件在 state 更改时重新渲染。我们不建议在
shouldComponentUpdate()
中进行深层比较或使用JSON.stringify()
。这样非常影响效率,且会损害性能。 -
render()
: render() 方法是 class 组件中唯一必须实现的方法。 -
getSnapshotBeforeUpdate()
: 在最近一次渲染输出(提交到 DOM 节点)之前调用。 -
componentDidUpdate()
: 在更新后会被立即调用。
render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。
1.3 Unmounting过程
componentWillUnmount()
: 在组件卸载及销毁之前直接调用
和装载过程与更新过程不一样,这个函数没有配对的 Did 函数,就1个函数,因为 卸载完就完了,没有“卸载完再做的事情”
componentWillUnmount()
会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount()
中创建的订阅等。
componentWillUnmount()
中不应调用 setState()
,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。
1.4 生命周期测试
直接使用create-app脚手架创建一个react基础项目
为了方便测试,需要提前移除index.js下的严格模式。
在 React 的严格模式 (<React.StrictMode>) 下,某些生命周期方法会被调用两次。这是为了帮助开发者发现副作用和潜在的问题。具体来说,以下生命周期方法会被调用两次:
constructor
render
componentDidMount
componentDidUpdate
这不会影响生产环境,只会在开发环境中启用。
如果你想避免这种行为,可以暂时移除严格模式:
1.4.1一个可以挂载和卸载的时钟
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
//<React.StrictMode> 严格模式下,React 会执行两次组件的 render 方法,以帮助发现潜在的副作用
<App />
//</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Clock.js
import React from 'react';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() { // 组件挂载时执行
this.timerID = setInterval(
() => this.tick(),
1000
);
console.log('组件挂载时执行');
}
componentWillUnmount() { // 组件卸载时执行
clearInterval(this.timerID);
console.log('组件卸载时执行');
}
tick() { // 每秒执行一次
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
}
}
export default Clock;
App.js 这里面有一个button 可以加载Clock组件和卸载Clock组件
import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import Clock from './Clock';
function App() {
const [showClock, setShowClock] = useState(true);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
{showClock && <Clock />}
<button onClick={() => setShowClock(!showClock)}>
{showClock ? '卸载 Clock 组件' : '加载 Clock 组件'}
</button>
</header>
</div>
);
}
export default App;
先挂载点击卸载后卸载
1.4.2 一个可以更新的按钮
Button,jsx
import React, { Component } from 'react';
class Button extends Component {
constructor(props) {
super(props);
this.state = { data: 0 };
this.setNewNumber = this.setNewNumber.bind(this);
}
setNewNumber() {
this.setState({ data: this.state.data + 1 });
}
render() {
return (
<div>
<button onClick={this.setNewNumber}>INCREMENT</button>
<Content myNumber={this.state.data} />
</div>
);
}
}
class Content extends React.Component {
componentDidMount() {
console.log("Component DID MOUNT!");
}
shouldComponentUpdate(newProps, newState) {
return true;
}
componentDidUpdate(prevProps, prevState) {
console.log("Component DID UPDATE!");
}
componentWillUnmount() {
console.log("Component WILL UNMOUNT!");
}
render() {
return (
<div>
<h3>{this.props.myNumber}</h3>
</div>
);
}
}
export default Button;
App.js
import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import Button from './Button';
function App() {
const [showButton, setShowButton] = useState(true);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
{showButton && <Button />}
<button onClick={() => setShowButton(!showButton)}>
{showButton ? '卸载 Button 组件' : '加载 Button 组件'}
</button>
</header>
</div>
);
}
export default App;
先挂载点击按钮后更新,点击卸载后卸载
参考
react.js - React新生命周期getDerivedStateFromProps的理解与使用 - 个人文章 - SegmentFault 思否
React.Component – React (reactjs.org)