本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~
理解React的生命周期
参考资料
React官网:State and Lifecycle
React官网:React.Component
微信公众号(译文):如何通俗易懂地解释React生命周期方法?
公众号原文:The (new) React lifecycle methods in plain, approachable language
- 挂载(mount)
- 更新(update)
- 卸载(unmount)
- 错误处理(error handle)
- 与mounting相关的生命周期方法:
render()
constructor(props)
componentDidMount()
static getDerivedStateFromProps(props, state)
- 与updating相关的:
static getDerivedStateFromProps(props, state)
shouldComponentUpdate(nextProps, nextState)
render()
getSnapshotBeforeUpdate(prevProps, prevState)
componentDidUpdate(prevProps, prevState, snapshot)
- 与unmounting相关的:
componentWillUnmount
- 与error handling相关的:
static getDerivedStateFromError(error)
componentDidCatch(error, info)
各个方法的作用在以上文档资料中已经讲解地很详细,这里不再赘述,这篇文章主要讨论组件在状态更新(props或state改变)时,生命周期方法的执行顺序
为此我创建一个demo:react-lifecycle-demo
- 下载到本地,首先在项目目录下运行命令
yarn
安装所需要的依赖包 - 运行
yarn start
命令,启动项目 - F12打开控制台查看打印结果
实验结果
页面代码:
// main.js 入口文件
import * as React from "react";
import { render } from "react-dom";
import HelloWorld, { ErrorBoundary } from "./component";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 1,
}
this.add = this.add.bind(this);
}
add() {
this.setState((state) => {
return { num: state.num + 1 };
})
}
render() {
return (
<div>
<ErrorBoundary>
{
this.state.num < 4
? <HelloWorld num={this.state.num} />
: <div>HelloWorld Component Unmounted</div>
}
<span style={{ color: "white", backgroundColor: "skyblue", cursor: "pointer" }} onClick={this.add}>+</span>
</ErrorBoundary>
</div>
)
}
}
render(<App />, document.querySelector("#root"));
// component.js
import * as React from "react";
export default class HelloWorld extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "HelloWorld!",
}
console.log("constructor");
}
componentDidMount() {
console.log("DidMount");
}
static getDerivedStateFromProps(state, props) {
console.log("getDerivedStateFromProps");
return { text: "from getDerivedStateFromProps" }
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
if (nextProps.num < 3) {
return true;
} else {
return false;
}
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate");
return "snapshot from getSnapshotBeforeUpdate";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("DidUpdate");
console.log(snapshot);
}
componentWillUnmount() {
console.log("WillUnmount")
}
render() {
console.log("render");
return (
<div>{`${this.props.num ? this.props.num : "none"} | ${this.state.text}`}</div>
)
}
}
export class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
static getDerivedStateFromError(error) {
console.log("getDerivedStateFromError");
return { hasError: true };
}
componentDidCatch(error, info) {
console.log("componentDidCatch");
}
render() {
return (
this.state.hasError
? <div>Something has went wrong</div>
: this.props.children
)
}
}
- 启动项目后,页面显示:
控制台打印:
> constructor
> getDerivedStateFromProps
> render
> DidMount
- 由此可知组件构建的经过的生命周期顺序
- state.text 属性从“HelloWorld!”变为“from getDerivedStateFromProps”
getDerivedStateFromProps
在组件首次构建时先于render
,返回的对象用于更新组件的state
- 第一次点击“+”
页面显示:
控制台打印:
> getDerivedStateFromProps
> shouldComponentUpdate
> render
> getSnapshotBeforeUpdate
> DidUpdate
> snapshot from getSnapshotBeforeUpdate
第一次点击“+”,更新了父组件App
的state
的num
属性,进而更新了子组件HelloWorld
的props
,所以第一次点击属于HelloWorld
组件的更新阶段
- 从控制台的打印结果可以知道组件updating时生命周期方法的调用顺序
- 更新阶段
constructor
和componentDidMount
不调用getSnapshotBeforeUpdate
在render
之后调用,即页面组件渲染完成后
- 第二次点击
页面:
控制台:
> getDerivedStateFromProps
> shouldComponentUpdate
- 第二次仍然是更新阶段,此时父组件app的state的num是3,子组件的props的num也为3,但是因为在shouldComponentUpdate方法中进行了条件判断,当this.props.num不小于3时即返回false,所以组件不再更新,则render,getSnapshotBeforeUpdate,componentDidUpdate等方法不再被调用
- 第三次点击
页面:
控制台:
> WillUnmount
- 第三次点击时组件的卸载阶段,因为父组件的state的num不小于4时,即卸载HelloWorld组件,展示
<div>HelloWorld Component Unmounted</div>
元素- 卸载阶段仅调用
componentWillUnmount
setState()
setState能触发组件更新
假设我们在子组件的componentDidMount
中使用setState来更新其state的text值为“setState from componentDidMount”,组件的构建过程是什么样的呢?
修改的代码如下:
componentDidMount() {
console.log("DidMount");
+ this.setState({
+ text: "setState from componentDidMount",
+ })
}
static getDerivedStateFromProps(state, props) {
console.log("getDerivedStateFromProps");
- return { text: "from getDerivedStateFromProps" }
+ return null;
}
重新启动项目,得到页面:
控制台:
> constructor
> getDerivedStateFromProps
> render
> DidMount
> getDerivedStateFromProps
> shouldComponentUpdate
> render
> getSnapshotBeforeUpdate
> DidUpdate
> snapshot from getSnapshotBeforeUpdate
"DidMount"被打印出来后,紧接着进行setState方法,此时组件进入updating阶段,组件被重新渲染(已挂载),所以经历的周期函数与this.props更新是一样的