ReactWheels07-01组件的两种写法Class和函数

Class组件写法

index.tsx

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
51
52
53
54
55
56
57
58
import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";
interface Props {
message: string;
}
interface State {
n: number;
}
class App extends React.Component<Props, State> {
static defaultProps = {
message: "hello"
};
static displayName = "Hjx";
y: () => void;
constructor(props) {
super(props);
this.state = {
n: 1
};
this.y = this.x1.bind(this);
// 等价于 this.y = ()=> this.x1.bind(this)
}
x = () => {
this.setState({
n: this.state.n + 1
});

// 这种写法相当于 this.y = this.x1.bind(this);
};
x1() {
console.log(this);
//1 当前实例
//2 undefined
// 答案是 2
// react 不帮你绑定 this
// 你只能 onClick={this.x1.bind(this)}
}
render() {
return (
<div className="App">
<div>{this.props.message}</div>
<div>{this.state.n}</div>
<button onClick={this.x}>+1</button>
<br />
<button onClick={this.x1}>默认不绑定this</button>
<br />
<button onClick={this.x1.bind(this)}>bind(this)绑定this</button>
<br />
<button onClick={this.y}>内部声明的y函数</button>
</div>
);
}
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

理解

1
2
3
4
5
6
7
class App extends React.Component<Props, State> {

x() {
...
}
}
这样写相当于 x() 挂载在 原型上, 也就是多个实例共享一个 x()

推荐使用的方式

这种写法有缺陷,相当于 this.x = ()=>{} ,也就是每个实例有一个 x()

浪费内存

如何解决? 无解

1
2
3
4
5
x = () => {
this.setState({
n: this.state.n + 1
});
};

defaultProps

  • 为 props 设置默认值,就是当用户不传递 message的时候
1
2
3
static defaultProps = {
message: "hello"
};

displayName

1
static displayName = "Hjx";

如果我们的用户是 JS 呢?

  • 它绕过类型检查传递一个 message=1 数字1 如何处理
  • 你需要安装依赖 prop-types 用来检查 js 传参的类型
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
import React from "react";
import ReactDOM from "react-dom";
// 引入 prop-types
import PropTypes from "prop-types";
import "./styles.css";
interface Props {
message: string;
}
interface State {
n: number;
}
class App extends React.Component<Props, State> {
static defaultProps = {
message: "hello"
};
static displayName = "Hjx";
// 使用 prop-types 限制用户传递的 message 类型
static propTypes = {
message: PropTypes.string
};
y: () => void;
constructor(props) {
super(props);
this.state = {
n: 1
};
}

render() {
return (
<div className="App">
<div>{this.props.message}</div>
</div>
);
}
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

React 也有计算属性

index.tsx

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
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
interface Props {
message: string;
}
interface State {
firstName: string;
lastName: string;
}
class App extends React.Component<Props, State> {
static defaultProps = {
message: "hello"
};
// 除了可以用计算属性
get name() {
return this.state.firstName + " " + this.state.lastName;
}
// 还可以给计算属性 赋值
set name(newName) {
const [firstName, lastName] = newName.split(" ");
this.setState({
firstName,
lastName
});
}
constructor(props) {
super(props);
this.state = {
firstName: "Frank",
lastName: "Aaa"
};
}
render() {
return (
<div className="App">
<div>{this.props.message}</div>
<div>{this.name}</div>
</div>
);
}
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

函数组件

  • 代码
  • 注意!! 必须 React在 16.8及以上版本才能用 state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";
interface Props {
message: string;
}
const App: React.FunctionComponent<Props> = (props) => {
return (
<div className="App">
Hello
</div>
);
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

使用 state

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import React, { useState } from "react";
    import ReactDOM from "react-dom";

    import "./styles.css";
    interface Props {
    message: string;
    }
    const App: React.FunctionComponent<Props> = props => {
    const [n, setN] = useState(1);
    const x = () => {
    setN(n + 1);
    };
    return (
    <div className="App">
    <div>{props.message}</div>
    <div>{n}</div>
    <button onClick={x}>+1</button>
    </div>
    );
    };

    const rootElement = document.getElementById("root");
    ReactDOM.render(<App message="hello" />, rootElement);

完整的 函数组件

  • 代码
  • useState 来作 state.n 的初始化和设置

    1
    2
    3
    4
    5
    // useState(1) 初始化 n = 1;
    const [n, setN] = useState(1);
    const x = () => {
    setN(n + 1);
    };
  • useEffect 来监听 state 改变 第二个参数为[监听的state]

  • useEffect 做 once 操作 第二个参数为[] mounted操作
  • useEffect 做组件销毁的处理, return 里写处理
  • prop-types 做js参数类型检查
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
51
52
53
54
55
56
57
58
59
60
61
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import PropTypes from "prop-types";
import "./styles.css";
interface Props {
// message 可有可无 此时有个坑
// 你在 函数组件里 props.message.split(' ')
// 报错不通过
message?: string;
}
const App: React.FunctionComponent<Props> = props => {
const [n, setN] = useState(1);
const [m, setM] = useState(2);
const x = () => {
setN(n + 1);
};
const y = () => {
setM(m + 1);
};
// 在每次UI更新之后执行 mounted或 updated之后
/*
useEffect(() => {
console.log("UI更新之后,mounted或 updated之后");
});
*/
// 单独监听 n 的改变
/*
useEffect(() => {
console.log("UI更新之后,n");
}, [n]);
*/
useEffect(() => {
console.log("只执行一次");
// 组件死之前想做什么 就 return
return () => console.log("我死了");
}, []);
// 因为 你定义的 message?:string 代表可有可无
// props.message.split(" ");
// 你只能用以下两种方式 强制断言
(props.message as string).split(" ");
props.message!.split(" ");
return (
<div className="App">
<div>{props.message}</div>
<div>{n}</div>
<button onClick={x}>n+1</button>
<div>{m}</div>
<button onClick={y}>m+1</button>
</div>
);
};

App.defaultProps = {
message: "default message"
};
App.displayName = "Hjx";
App.propTypes = {
message: PropTypes.string
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

两种组件方式的区别

  • 16.8.0及以上 才可以使用 useState / useEffect API 也就是 hooks API
  • 16.8.0以前的函数组件为 无状态组件
  • 16.8.0以后的函数组件可以代替 class组件

解决 props可有可无

使用解构赋值,但是这样代码不好理解

1
2
3
4
const App: React.FunctionComponent<Props> = (
{message = 'default message'}) => {
props.message.split(" ");
};

函数组件和class组件区别

函数 vs
无状态(useState) 有状态(state)
可以实现函数式 很难实现函数式
setN执行之后原来的状态[n,setN]等都不要了,会得到一个新的[n,setN] this.setState({n:1}) ,n一直在this.state.n
迭代,每次得到一个最新的状态,之前的全部不要了 在一个对象上不停的进行赋值,修改的都是同一个对象

函数式的本质就是 不能进行第二次赋值

  • 函数式好处就是 可以使用数学知识
  • 面向对象的好处是 适合人类
1
2
3
4
a = 1
a = 2 就不行

而面向对象可以