记 React 中一次复用与封装

前提

最近发现自己一直在写很多重复性的代码,比例公司的某个项目的搜索栏有很多的下拉选择器,而我的做法是不停的初始化store、配置api、引入select组件、配置select组件,这还只是其中一个下拉选择器,如果有多个就觉得这么写是不是很傻,所以最近一直在考虑如果优化。

当重复性的工作过多的时候,就应该考虑自己的代码写的是不是有问题。

封装:把客观事物封装成抽象的类,隐藏属性和方法的实现细节,仅对外公开接口。

初步想法是减少重复性的工作,而目前我能想到的解法方法有几个:

  • 用 class 抽象成一个公共的组件(这种最简单也最实用)
  • 用 HOC(高阶组件)抽象成一个公共的组件
  • 用 Render Props (函数子组件 FaCC)抽象成一个公共的组件

用高阶组件实现

编写 componentSelect 组件

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
import React, { Component } from "react";
import { Select } from "antd";
import axios from "axios";

const WithClassName = WrappedComponent => {
return class HOC extends Component {
constructor(props) {
super(props);
this.state = {
list: []
};
}
componentDidMount() {
this.getData();
}
render() {
let { list } = this.state;
let { valueName, labelName } = this.props;
return (
<WrappedComponent {...this.props}>
{list.map(it => (
<WrappedComponent.Option value={it[valueName]} key={it[valueName]}>
{it[labelName]}
</WrappedComponent.Option>
))}
</WrappedComponent>
);
}
getData() {
let { params = {}, api = "" } = this.props;
axios
.get(api, {
...params
})
.then(it => {
if (it.resultObject != null) {
this.setState({
list: it.resultObject
});
}
})
.catch(e => {
console.log(e);
});
}
};
};

export default WithClassName(Select);

使用 componentSelect 组件

1
2
3
4
5
6
7
8
9
10
import ComponentSelect from 'componentSelect'

<ComponentSelect
api="/api/getSelectData"
placeholder="请选择下拉"
allowClear={true}
valueName={'valueCode'}
labelName={'valueName'}
params={{ ...params }}
/>

用 Render Props (函数子组件 FaCC)实现

编写 SelectContext 组件

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
import React, { Component } from "react";
import axios from "axios";

class SelectContext extends Component {
constructor(props) {
super(props);
this.state = {
list: []
};
}
componentDidMount() {
this.getData();
}
render() {
const { list } = this.state;
const { children } = this.props;
return children(list);
}
getData() {
const { params = {}, api = "" } = this.props;
axios.http
.post(api, {
...params
})
.then(it => {
if (it.resultObject != null) {
this.setState({
list: it.resultObject
});
}
})
.catch(e => {
console.log(e);
});
}
}

export default SelectContext;

使用 SelectContext 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Select } from "antd";
import SelectContext from 'componentSelect'

<SelectContext
api="/api/getSelectData"
params={{...params}}
>
{list => (
<Select placeholder="请选择" allowClear={true}>
{list.map(it => (
<Select.Option value={it.value} key={it.value}>
{it.name}
</Select.Option>
))}
</Select>
)}
</SelectContext>

注意

在 ant.design 表单中直接使用 Render Props 会出现无法取到值的情况,需要改成如下写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<SelectContext api="/api/getSelectData" params={{ ...params }}>
{list =>
<Form.Item>
{getFieldDecorator("value", {})(
<Select placeholder="请选择" allowClear={true}>
{list.map(it => (
<Select.Option value={it.value} key={it.value}>
{it.name}
</Select.Option>
))}
</Select>
)}
</Form.Item>
}
</SelectContext>;

目前暂时不知道为何会这样,我猜应该跟 ant.design 的写法有关系