local state的管理
- 之前讨论的 redux、rematch、easy-peasy 都是全局的状态管理
- 对于大部分的简单业务,local state的管理并不麻烦,基本上就是控制一些弹窗的展示,loading的展示等
常见例子
- 例子中的逻辑是一个组件获取接口数据,并根据接口状态展示不同信息;
- 例子中是一个class组件,组件中既包含view视图,又包含model数据处理(状态存储和状态修改逻辑)
- 这样的写法优点是逻辑紧密关联,易于理解;缺点是逻辑难以复用javascript
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
41import React from 'react';
import axios from 'axios'
class PageA extends React.Component {
/**
* loading 是否显示loading
* data 数据
* err 请求错误
*/
state = {
loading: false,
data: null,
err: null
}
async componentDidMount() {
this.setState({ loading: true })
try {
const res = await axios.get('/api/pageA/options')
this.setState({
loading: false,
data: res.data.data,
})
} catch (err) {
this.setState({
loading: false,
error: err.message
})
}
}
render() {
if (this.state.loading) {
return <div>loading....</div>
} else {
return <div>{this.state.data}</div>
}
}
}
export default PageA;
class组件的逻辑复用:容器组件和视图组件分离
视图组件复用
- 容器组件只负责处理状态
- 视图组件之负责展示
- 一般UI组件库使用这种方式javascript
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
49import React from 'react';
import axios from 'axios'
// 视图组件
class Loading extends React.Component {
render() {
if (this.props.loading) {
return <div>loading....</div>
} else {
return <div>{this.props.data}</div>
}
}
}
// 容器组件
class PageA extends React.Component {
/**
* loading 是否显示loading
* data 数据
* err 请求错误
*/
state = {
loading: false,
data: null,
err: null
}
async componentDidMount() {
this.setState({ loading: true })
try {
const res = await axios.get('/api/pageA/options')
this.setState({
loading: false,
data: res.data.data,
})
} catch (err) {
this.setState({
loading: false,
error: err.message
})
}
}
render() {
return <Loading {...this.state}></Loading>
}
}
export default PageA;
容器组件复用
- 高阶组件HOCTypescript的支持并不友好,代码可读性差javascript
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
51import React from 'react';
import axios from 'axios'
// 视图组件
class Loading extends React.Component {
render() {
if (this.props.loading) {
return <div>loading....</div>
} else {
return <div>{this.props.data}</div>
}
}
}
// 容器组件
function withLoading(Component) {
return class PageA extends React.Component {
/**
* loading 是否显示loading
* data 数据
* err 请求错误
*/
state = {
loading: false,
data: null,
err: null
}
async componentDidMount() {
this.setState({ loading: true })
try {
const res = await axios.get('/api/pageA/options')
this.setState({
loading: false,
data: res.data.data,
})
} catch (err) {
this.setState({
loading: false,
error: err.message
})
}
}
render() {
return <Component {...this.state}></Component>
}
}
}
export default withLoading(Loading); - renderProps会导致嵌套过多javascript
1
2
3
4
5<WithLoading>
{(props) => {
<Loading {...props} />
}}
</WithLoading>
React Hook 实现逻辑复用
优势
- React Hook的一大特色就是状态逻辑复用
- 而且社区上已经积攒了很多的hook可以直接使用javascript
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
33import React, { useState, useEffect } from 'react';
import axios from 'axios'
function useLoading() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
useEffect(() => {
setLoading(true);
axios.get('/api/pageA/options').then(res => {
setLoading(false);
setData(res.data.data);
}).catch(err => {
setLoading(false);
setError(err.message)
})
}, [])
return [loading, error, data]
}
function PageA() {
const [loading, error, data] = useLoading();
if (loading) {
return <div>loading....</div>
} else {
return <div>{data}</div>
}
}
export default PageA;
不足
- 在function里组织业务代码,调用函数的时候需要注意函数声明的顺序
- 需要规范hook代码顺序javascript
1
2
3
4
5
6function APP(){
// 1. useState, useRef
// 2. 组件函数
// 3. useEffect
// 4. render逻辑
}