09.类组件

动态组件(类组件的使用)

复习ES6Moudle导入知识:

import React from "react"import {Component,PureComponent}...

import {Component,PureComponent}...不是对React对象进行解构赋值。

创建类

创建一个,并继承React.Component/PureComponent,基于render返回JSX视图

创建一个构造函数(类)的过程:

  • 继承React.Component/PureComponent这个类,一般使用ES6中的class创建类(因为方便)

  • 给当前类设置一个render的方法(放在其原型上):在render方法中,返回需要渲染的视图

第一步、继承

1
2
3
4
5
6
7
8
9
10
//ES5写法(寄生组合继承,可不看)
function AAA() {
React.Component.call(this);//cell继承React.Component
this.state = {
a: 1
}
}
Object.setPrototypeOf(AAA.prototype, React.Component.prototype);//原型继承
AAA.prototype.sum = function () {
}//原型上的方法
1
2
3
4
//ES6写法
class AAA extends React.Componet{
}
exprot default AAA;

第二步、设置Render方法

于是完整的写法:

1
2
3
4
5
6
7
8
9
import React from "react";
class ClassComponent extends React.Component {
render() {
return <div>
我是类组件
</div>;
}
};
export default ClassComponent;

类组件的动态化

继承知识

类组件是动态组件

  • 具备属性及规则校验
  • 具备状态,修改状态可以控制视图更新
    • setState
    • forceUpdate
  • 具备周期函数
    • 严格模式下,一些不安全的周期函数是禁止使用的

PureComponent /pjʊə(r)/

一个小测试:新状态值和原始状态值是相同的堆内存地址「数据发生改变」,我们可以试试继承React.Component和React.PureComponent的区别?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default class Demo extends React.Component {
state = {
arr: [10, 20, 30]
};
handler = () => {
this.state.arr.push(40);
this.setState({ arr: this.state.arr });
};
render() {
return <div className="demo">
{this.state.arr.join('+')}
<button onClick={this.handler}>更改</button>
</div>;
}
};

自动增加shouldComponentUpdate周期函数,并对原始属性/状态和最新属性/状态进行浅比较

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
const shallowEqual = function shallowEqual(objA, objB){
if (Object.is(objA, objB)) return true;
if (typeof objA !== 'object' || objA === null ||
typeof objB !== 'object' || objB === null) {
return false;
}
const keysA = Reflect.ownKeys(objA),
keysB = Reflect.ownKeys(objB);
if (keysA.length !== keysB.length) return false;
for (let i = 0; i < keysA.length; i++) {
let key = keysA[i];
if (
!objB.hasOwnProperty(key) ||
!Object.is(objA[key], objB[key])
) {
return false;
}
}
return true;
};

// 测试使用
export default class Demo extends React.Component {
...
shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
}
...
};

非受控组件

不受状态管控的组件「可以基于ref获取DOM元素进行操作」

  • ref=’xxx’ 不推荐使用
  • ref={x=>this.xxx=x}
  • React.createRef

赋值给标签,目的是获取DOM元素;
赋值给类组件,目的是获取组件的实例;
函数组件不能直接设置ref,但是可以基于React.forwardRef /ˈfɔːwəd/实现ref转发!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 被调用的子组件
import React from "react";
const Demo3 = function Demo3(props, ref) {
return <div className="demo3"
ref={ref}>
</div>;
};
export default React.forwardRef(Demo3);

// 父组件
export default class Demo extends React.Component {
componentDidMount() {
console.log(this.box); //-> div.demo3
}
render() {
return <div className="demo">
<Demo3 ref={x => this.box = x} />
</div>;
}
};

关于setState的进阶研究

React18中,对于setState的操作,采用了 批处理

  • 构建了队列机制
  • 统一更新,提高视图更新的性能
  • 处理流程更加稳健

在React 18之前,我们只在 React合成事件/周期函数期间批量更新;默认情况下,React中不会对 promise、setTimeout、原生事件处理(native event handlers)或其它React默认不进行批处理的事件进行批处理操作!

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
import React from "react";
export default class Demo extends React.Component {
state = {
x: 10,
y: 5,
z: 0
};
handler = () => {
let { x, y, z } = this.state;
this.setState({ x: x + 1 });
this.setState({ y: y + 1 });
console.log(this.state);
setTimeout(() => {
this.setState({ z: z + 1 });
console.log(this.state);
});
};
render() {
console.log('render');
let { x, y, z } = this.state;
return <div className="demo">
{x}-{y}-{z}
<br />
<button onClick={this.handler}>处理</button>
</div>;
}
};

setState接收的参数还可以是一个函数,在这个函数中可以拿先前的状态,并通过这个函数的返回值得到下一个状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default class Demo extends React.Component {
state = {
num: 0
};
componentDidMount() {
for (let i = 0; i < 100; i++) {
/* this.setState({ num: this.state.num + 1 });
console.log(this.state.num); */
this.setState(prevState => {
return {
num: prevState.num + 1
};
});
}
}
render() {
console.log('render');
let { num } = this.state;
return <div className="demo">
{num}
</div>;
}
};

基于flushSync可以清空队列

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 from "react";
import { flushSync } from 'react-dom';
export default class Demo extends React.Component {
state = {
x: 10,
y: 5,
z: 0
};
handler = () => {
let { x, y, z } = this.state;
this.setState({ x: x + 1 });
flushSync(() => {
this.setState({ y: y + 1 });
});
console.log(this.state); // x:11 y:6 z:0 -> render

this.setState({ z: z + 1 });
console.log(this.state);
};
render() {
console.log('render');
let { x, y, z } = this.state;
return <div className="demo">
{x}-{y}-{z}
<br />
<button onClick={this.handler}>处理</button>
</div>;
}
};