高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
具体而言,高阶组件是参数为组件,返回值为新组件的函数。
最近在做项目的时候遇到了一个情形:在系统后台存在多个全局设置开关,例如在多个页面都需要请求会员设置的开关。如果在每个需要调用全局设置的地方都去请求一下接口,就会有一种不优雅的感觉,这个时候我就想到利用高阶组件抽象一下。
版本一
getMemberSettingHoc = Component => class ContainerComponent extends React.Component {
state = {
loading: true,
memberSetting: ''
}
componentDidMount() {
Api.getSetting().then(memberSetting => {
this.setState({ memberSetting })
})
...doSomething
}
render() {
return (
<Component {...this.props} memberSetting={this.state.memberSetting} />
)
}
}
// 使用高阶组件
class Home extends Component {
...
}
export default getMemberSettingHoc(Home);
这个时候看起来编写一个 getMemberSettingHoc 的高阶组件可以让我们复用获取会员设置的逻辑
版本二
这个时候如果又多了几个全局开关,例如:交易设置,支付设置,预约设置…
问题来了,这个时候我们还需要编写更多的高阶组件来抽象这些设置吗?
答案是否定的,我们可以实现一个 preMountHoc 来处理这些请求,顾名思义, preMountHoc 就是在挂载之前做事情的高阶组件。
代码如下
const preMountDecorator = doSomething => ContentComponent => class PreMountComponent extends React.Component {
}
我们传入了一个参数,这个参数来传入要请求的接口 doList 和返回的字段 keyList ,在 PreMountComponent 挂载的时候去 doSomething, 然后通过props,传回给传入的 ContentComponent.
我们看下代码
// preMountHoc
const preMountHoc = doSomething => ContentComponent => class PreMountComponent extends React.Component {
constructor() {
super();
this.state = {
loading: true,
result: {},
};
}
componentDidMount() {
const doList = get(doSomething, 'doList', []);
const doListLength = get(doList, 'length') || 0;
const keyList = get(doSomething, 'keyList', []);
const keyListLength = get(keyList, 'length') || 0;
if (doListLength == 0) {
this.setState({ loading: false });
return;
}
Promise.all(doList).then((res) => {
const result = {};
if (doListLength == keyListLength) {
keyList.forEach((el, index) => {
result[el] = res[index];
});
} else {
doList.forEach((el, index) => {
result[`pre_${index}`] = res[index];
});
}
this.setState({ result, loading: false });
});
}
render() {
const { loading, result } = this.state;
if (loading) {
return <BlockLoading loading />;
}
return (
<ContentComponent {...result} {...this.props} />
);
}
};
export default preMountHoc;
给 preMountHoc 传入的需要的请求和返回的字段名,就可以达到我们的目的,来看看使用方法
class ScrmList extends Component {
...
}
export default preMountHoc({
doList: [settingApi.getPower(), settingApi.hasMemberThreshold()],
keyList: ['memberStoreSetting', 'scrmConditionType'],
})(ScrmList)
当然高阶组件也是装饰器,你也可以这么用
@preMountHoc({
doList: [settingApi.getPower(), settingApi.hasMemberThreshold()],
keyList: ['memberStoreSetting', 'scrmConditionType'],
})
class ScrmList extends Component {
...
}
export default ScrmList;
总结
高阶组件并不是 React 的特性,是函数式编程的一种范式,最大的好处就是解耦和灵活性,在这分享下最近项目中的思考。