react类组件

创建 Class 组件

  1. ES6 方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import React from 'react'

    class B extends React.Component{
    constructor(props){
    super(props)
    }
    render(){
    return (<div>hi</div>)
    }
    }
    export default B
  1. ES5 方式(过时)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import React from 'react'

    const A = React.createClass({
    render(){
    return(<div>hi</div>)
    }
    })
    export default A
    // 由于 ES5 不支持 class,才会有这种方法

props

传入 props 给 B 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Parent extends React.Component {
constructor(props){
super(props)
this.state = {name: 'frank'}
}
onClick = () => {}
render(){
return(
<B name={this.state.n} onClick={this.onClick}>hi</B>
)
}
}
// 外部数据被封装为一个对象
// {name: 'frank', onClick:...,children: 'hi'}

###初始化

1
2
3
4
5
6
7
class B extends React.Component{
constructor(props){
super(props)
}
render(){...}
}
// this.props 就是外部数据 对象的地址 了

读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class B extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div onClick={this.props.onClick}>
{this.props.name}
<div>{this.props.chrldren}</div>
</div>
)
}
}
// 通过 this.props.xxx 读取

props 的作用

接受外部数据

  • 只能读不能写
  • 外部数据由父组件传递

接受外部函数

  • 在恰当的时机,调用该函数
  • 该函数一般是父组件的函数

state & setState

初始化

1
2
3
4
5
6
7
8
9
10
11
12
class B extends React.Component {
constructor(props){
super(props)
this.state = {
user: {
name: 'frank',
age: 18
}
}
}
render(){...}
}

读写 State

this.state

this.state.user.name


this.setState(???,fn)

this.setState(newState, fn)

注意 setState 不会立刻改变 this.state,会在当前代码运行完后,再去更新 this.state,从而触发 UI 更新。(异步)

this.setState((state, props) => newState, fn)

这种方式的 state 更容易理解

fn 会在写入成功后执行。

shallow merge: setState 会自动将新的 state 与 旧的 state 进行一级合并。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
onClick = () => {
this.setState({ x: this.state.x + 1 });
};

onClick2 = () => {
this.setState( (state) => ({ x: state.x + 2 }) );
};


onClick = () => {
this.setState(
{
x: this.state.x + 1,
},
// callback
() => {
this.setState({ x: this.state.x + 1 });
}
);
};

修改 this.state 的属性值,不推荐用!

1
2
this.state.x += 1
this.setState(this.state)

生命周期

生命周期图谱


类比如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let div = document.createElement('div')
这是 div 的 create / construct 过程

div.textContent = 'hi'
这是初始化 state

document。body.appendChild(div)
这是 div 的 mount 过程

div.textContent = 'hi2'
这是 div 的 update 过程

div.remove()
这是 div 的 unmount 过程

函数列表

constructor()

static getDerivedStateFromProps()

shouldComponentUpdate()

render()

getSnapshotBeforeUpdate()

componentDidMount()

componentDidUpdate()

componentWillUnmount()

static getDerivedStateFromError()

componentDidCatch()


函数列表-必会

constructor() - 在这里初始化 state

shouldComponentUpdate() - return false 阻止更新

render() - 创建虚拟 DOM

componentDidMount() - 组件已出现在页面

componentDidUpdate() - 组件已更新

componentWillUnmount() - 组件将死


constructor

用途:

初始化 props

初始化 state,但此时不能调用 setState

用来写 bind this

1
2
3
4
constructor(){
...
this.onClick = this.onClick.bind(this)
}

可以用新语法代替

1
2
constructor(){...}
onClick = () => {}

shouldComponentUpdate

用途:

返回 true 表示不阻止 UI 更新

返回 false 表示阻止 UI 更新

面试常问: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
31
32
33
34
import React from "react";

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
n: 1,
};
}
onClick = () => {
this.setState((state) => ({ n: state.n + 1 }));
this.setState((state) => ({ n: state.n - 1 }));
};
// {n:1} 和 {n:1}
shouldComponentUpdate(nextProps, nextState) {
// 阻止渲染
if (nextState.n === this.state.n) {
return false;
} else {
return true;
}
}
render() {
console.log("render了一次");
return (
<div>
App组件<br />
{this.state.n}
<button onClick={this.onClick}>+1</button>
</div>
);
}
}
export default App;

可以使用 React.PureComponent 替代 React.Component 实现 shouldComponentUpdate()

PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。
如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。

官方文档

注意

React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。

仅在你的 props 和 state 较为简单时,才使用 React.PureComponent


render

用途:

展示视图 return (<div> ... </div>),只能有一个根元素。

两个根元素,要用 <React.Fragment> 包起,可以缩写为 <> </>

技巧:

render 里面可以写 if...else?:表达式

不能直接写 for循环,需要用数组

render 里面可以写 aray.map(循环)

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
n: 1,
array: [1, 2, 3]
};
}
onClick = () => {
this.setState((state) => ({ n: state.n + 1 }));
};
// if...else
render() {
let message
if(this.state.n%2 === 0){
message = <span>偶数</span>
}else{
message = <span>奇数</span>
}
return(
<React.Fragment>
{message}
<button onClick={this.onClick}>+1</button>
</React.Fragment>
)
}
// ? :
render() {
return (
<>
{this.state.n % 2 === 0 ?
<span>偶数</span> :
<span>奇数</span>}
<button onClick={this.onClick}>+1</button>
</>
);
}
// for 循环
render() {
let result = [];
for (let i = 0; i < this.state.array.length; i++) {
result.push(this.state.array[i]);
}
return result;
}
// for 循环 map()
render() {
return this.state.array.map((n) => <span key={n}>{n}</span>);
}
}

componentDidMount()

用途:

在元素插入页面后执行代码,这些代码依赖 DOM

比如,想获取 div 的高度,最好在这里写。

此处可以发起加载数据的 Ajax 请求(官方推荐)

首次渲染 会执行此钩子

官方文档

1
2
3
4
5
6
7
8
9
10
11
12
// 想获取 div 的高度
...
componentDidMount(){
const div = document.querySelector('#xxx');
const {width} = div.getBoundingClientRect()
this.setState({width})
}
render() {
return(
<div id='xxx'>hello, width, {this.state.width}px</div>
)
}

改用 ref

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
class App extends React.PureComponent {
divRef = undefined;
constructor(props) {
super(props);
this.state = {
n: 1,
width: undefined,
};
this.divRef = React.createRef();
}

onClick = () => {
this.setState((state) => ({ n: state.n + 1 }));
};

componentDidMount() {
const div = this.divRef.current;
const { width } = div.getBoundingClientRect();
this.setState({ width });
}

render() {
return <div ref={this.divRef}>hello, width: {this.state.width}px</div>;
}
}

ref 官方文档


componentDidUpdate()

用途:

在视图更新后执行。第一次渲染不执行。

此处也可以发起 Ajax 请求,用于更新数据文档)。

首次渲染不会 执行此钩子。

在此处 setState 可能会引起无限循环,除非放在 if 里。

若 shouldComponentUpdate 返回 false,则不会触发此钩子。


componentWillUnmount()

用途:

组件将要 被移出页面 然后被销毁 时执行代码

unmount 过的组件不会再次 mount

举例:

  • 如果你在 c…DidMount 里面监听了 window scroll

    那么你就要在 c…WillUnmount 里面取消监听

  • 如果你在 c…DidMount 里面创建了 Timer

    那么你就要在 c…WillUnmout 里面取消 Timer

  • 如果你在 c…DidMount 里面创建了 Ajax 请求

    那么你就要在 c…WillUnmount 里面取消请求

  • 否则你就是菜逼。

官方文档


生命周期回顾

  • constructor() - 在这里初始化 state

  • shouldComponentUpdate() - return false 阻止更新

  • render() - 创建虚拟 DOM

  • componentDidMount() - 组件已出现在页面

  • componentDidUpdate() - 组件已更新

  • componentWillUnmount() - 组件将死