React 组件生命周期

React 组件生命周期

一月 20, 2021

组件生命周期

react 谷歌调试插件: react developer tools

  1. 组件的生命周期
    1. react 版本的生命周期: 15 版本 16 版本 17 版本【主流】
      1. react 15.x.x 15 版本生命周期
      2. react 16.0.0 - 16.3.x 16 版本
      3. react 16.3.0 以上 17 版本
    2. 除 15 版本以外,生命周期分为四个阶段
      1. 挂载阶段
        1. 特点: 自动执行,执行一次
      2. 更新阶段
        1. 触发条件: 组件调用了 setState || forceUpdate
      3. 卸载阶段
      4. 错误处理阶段【 15 版本是没有的 】

挂载阶段

  1. constructor
    • 通过 props 参数可以获得绑定在组件身上的数据
    • 可以定义 state
    • 可以为类中的普通函数绑定 this 指针
1
2
3
4
5
6
7
8
9
10
11
12
13
// P 为interface ts类型声明
constructor(props: P) {
// 作用3 props接收绑定在组件身上的属性
// console.log("props", props);

super(props);
// 作用1 定义state
this.state = {
name: "yyc",
};
// 作用2 普通函数的this绑定
this.handler = this.handler.bind(this);
}
  1. static getDerivedStateFromProps(props,state) { return {} }
    • 从 props 中派生 state
1
2
3
4
5
6
7
8
9
// 父组件绑定money属性给这个组件
// getDerivedStateFromProps 从属性中获取派生的状态
static getDerivedStateFromProps(props: P, state: S) {
console.log("getDerivedStateFromProps");
// 作用 return返回值可以将props处理成state
return {
money: props.money,
};
}
  1. render
    • 解析 this.state & this.props
    • 将 jsx 形式的虚拟 dom 转成对象形式的虚拟 dom
    • render 中不允许使用 this.setState() 如何你使用了,就会栈溢出
1
2
3
4
5
6
7
8
9
render() {
return (
<div>
<p> {this.state.name} </p> {/* 解析this.state */}
<p> {this.props.money} </p> {/* 解析this.props */}
<p ref={(el) => (this.p = el)}>asdds</p> {/* 渲染成对象形式的虚拟DOM */}
</div>
);
}
  1. componentDidMount
    • 组件挂载结束
    • 虚拟 DOM -> 真实 DOM
    • 获得到了真实 dom,可以做真实 DOM 操作
    • 数据请求发送
    • 第三方库实例化
1
2
3
4
5
6
7
8
9
10
11
12
componentDidMount() {
// 表示组件挂载结束
// 作用1: 将虚拟DOM 渲染成真实DOM,也就是可以做真实DOM处理
this.p.style.background = "red";
// 作用2: 发请求
fetch("http://59.110.226.77:3000/api/category")
.then((data) => data.json())
.then((res) => {
console.log("res", res);
})
.catch((error) => Promise.reject(error));
}

更新阶段

  1. static getDerivedStateFromProps 作用同挂载阶段
1
2
3
4
5
6
static getDerivedStateFromProps(props: P, state: S) {
// 挂载阶段和更新阶段都会执行
// 作用同挂载阶段 用来使props变成state
console.log("gdsfp");
return {};
}
  1. shouldComponentUpdate 表示组件是否应该更新
    1. return true 表示组件更新
    2. return false 表示组件不更新
    3. 功能
      1. 渲染优化: 新属性 vs 旧属性 || 新状态 vs 旧状态
      2. 比较手动来写
1
2
3
4
5
6
7
8
9
// 渲染优化
shouldComponentUpdate(nextProps: P, nextState: S) {
// 必须有返回值 返回是一个布尔值
// return true 表示允许组件重新渲染
// return false 表示不允许组件重新渲染
console.log("scu");
return this.state.name !== nextState.name;
}

  1. render 同挂载阶段
  2. getSnapShotBeforeUpdate 在更新前获取快照
1
2
3
4
5
6
7
getSnapshotBeforeUpdate = (prevProps: P, prevState: S) => {
console.log("gsbu");
// 必须有返回值
// 1. 可以得到旧的props和state
// 2. 返回值可以传递一个数据给componentDidUpdate
return 1000;
};
  • 它的返回值会传递给 componentDidUpdate 的第三个参数
  1. componentDidUpdate
    • 将再次生成的虚拟 DOM 和就的虚拟 DOM,通过 filber 进行比较,生成 patch 补丁对象
    • patch 补丁对象渲染为真实 DOM
    • 发送数据请求
    • 真实 dom 操作
    • 可以获取 getSnapShotBeforeUpdate 传递过来的数据
1
2
3
4
5
6
7
8
9
componentDidUpdate(prevProps: P, prevState: S, snapShot: number) {
// 表示组件更新阶段了
// 1. 再次得到新的真实DOM

// 2. 可以发请求

// 3. 通过snapShot得到getSnapshotBeforeUpdate传过来的值
console.log("我得到了", snapShot);
}

PureComponent vs Component

在 component 中如果渲染一样的值,都会触发 render()钩子函数,导致性能浪费

  1. PureComponent 默认会进行一次新旧 state 或者新旧 props 的对比,可以得到一个性能优化的目的
  2. 缺点:
    1. 它只能对比一层数据,所以我给了它一个专业词 浅比较
  3. 总结
    1. PureComponent 会自动进行一次浅比较
    2. Component 不会自动进行比较的,需要我们手动在 shouldComponentUpdate 中比较

forceUpdate

  1. 强制渲染,会跳过 shouldComponentUpdate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { Component } from "react";

export default class App extends Component {
name = "fox";

setName = () => {
this.name = "dog";
/*
不使用forceUpdate界面不会更新
因为他不是state 激活不了更新阶段
*/
this.forceUpdate();
};
render() {
return (
<div>
<button onClick={this.setName}>修改</button>
<p>{this.name}</p>
</div>
);
}
}

销毁阶段

  1. componentWillUnmount
    • 触发条件: 组件被销毁了
    • 作用
      • 清除计时器、定时器
      • 清除绑定在 window/document 身上的事件
      • 清除第三方实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, { Component } from "react";
import _ from "lodash";
export default class Hello extends Component {
timer: any = null;
lodash: any = null;

componentDidMount() {
this.lodash = _; // 第三方实例
this.timer = setInterval(() => {
// 计时器/定时器
console.log("hello", this.lodash);
}, 1000);
window.onresize = function () {
// windonw或者document对象
console.log("浏览器尺寸发生了改变");
};
}

componentWillUnmount() {
console.log("销毁了");
clearInterval(this.timer); // 销毁计时器
window.onresize = null; // 销毁window上的事件
delete this.lodash; // 销毁第三方实例
}

render() {
return <div>hello</div>;
}
}

错误处理阶段

  1. componentDidCatch
  2. 做什么的
    • 用于捕获子组件错误,然后显示回退 UI
  3. 写在哪里的?
    • 写在父组件中的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React, { Component } from "react";
import Hello from "./Hello";

interface P {}

interface S {
errorFlag: boolean;
}

export default class App extends Component<P, S> {
constructor(props: P) {
super(props);
this.state = {
errorFlag: false,
};
}

componentDidCatch(error: any, info: any) {
console.log("触发");
if (error) {
this.setState({
errorFlag: true,
});
}
}

render() {
if (this.state.errorFlag) return <div> 回退UI</div>;
return (
<div>
<Hello />
</div>
);
}
}

子组件在 componentDidMount 扔出一个错误

16 版本

  1. 挂载阶段
  2. componentWillMount[17 版本弃用了它,用了 static getDerivedStateFromProps]
  3. 更新阶段
  4. componentWillReceiveProps 因为 shouldComponentUpdate 也可以监听
  5. componentWillUpdate 因为造成异步滥用

15 版本

  1. 通过 React.createClass 来定义类组件的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from "react";
//todo 15版本是通过React.createClass来定义类组件的
export default React.createClass({
getDefaultProps() {
//todo 用于定义props的
return {
coloe: "黄皮肤",
};
},
getInitailState() {
//todo 用于定义state
return {
count: 1,
};
},
});

react 受控组件 & 非受控组件 & 半受控组件

受控组件 【表单元素】

  • 表单元素的 value 受组件的 state 的控制,这个表单元素我们就称之为受控组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React, { Component } from "react";

interface P {}

interface S {
val: string;
}

export default class App extends Component<P, S> {
constructor(props: P) {
super(props);
this.state = {
val: "",
};
}

setVal = (e: any) => {
this.setState({
val: e.target.value,
});
};
render() {
const { val } = this.state;
return (
<div>
<input type="text" value={val} onChange={this.setVal} />
</div>
);
}
}

非受控组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from "react";

export default class App extends Component {
render() {
return (
<div>
表单元素自己管理自己的state <hr />
我们可以认为input的value就是它自己的state <hr />
表单元素自己管理自己的state,我们称这个表单为 非受控组件 <hr />
{/* defaultValue input的默认值 */}
<input defaultValue="hello" />
</div>
);
}
}

半受控组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component } from "react";
interface P {}
interface S {
initVal: string;
}
export default class App extends Component<P, S> {
constructor(props: P) {
super(props);

this.state = {
initVal: "hello",
};
}

render() {
return (
<div>
表单元素的初始值是由组件来管理的 <hr />
表单元素value的变化还是交给了表单元素自己管理 <hr />
这种类型我们称之为 半受控组件 <hr />
<input defaultValue={this.state.initVal} />
</div>
);
}
}