简单来说高阶组件(HOC)就是一个函数,它接受一个组件作为参数然后返回一个新组件。HOC 主要用于组件之间逻辑复用。比如你写了几个组件,他们之间的逻辑几乎相同,就可以用 HOC 对逻辑进行封装复用。
本文主要分享:
- 如何封装 HOC
- HOC + 组合函数,处理多个 HOC 嵌套问题
- HOC + 柯里化,封装多参数的 HOC
如何封装 HOC
这里介绍几个常用的 HOC
withLogger
我们在调试代码时,经常都需要打印 props,所以可以将打印逻辑封装起来。
const withLogger = (prefix = "") => WrappedComponent => {
const WithLogger = props => {
console.log(`${prefix}[Props]:`, props);
return <WrappedComponent {...props} />;
};
return WithLogger;
};
复制代码
使用:
withLogger('这里打印的是xxx')(Component)
复制代码
withData
组件中的获取数据的逻辑也可以抽离成 HOC,需要传入 url, param 两个参数。
import React, { Component } from "react";
const withData = (url, params) => WapperedComponent => {
class WithData extends Component {
state = {
data: []
};
componentDidMount() {
fetch(url, {body: JSON.stringify(params)})
.then(response => response.json())
.then(data => this.setState({ data }));
}
render() {
return <WapperedComponent {...this.state} {...this.props} />;
}
}
return WithData;
};
复制代码
使用:
withData(
url: 'https://jsonplaceholder.typicode.com/posts',
params: {
_limit: 10,
page: 2
}
)(Component)
复制代码
withLoading
由于数据请求是异步的,为了不让用户看到一片空白,当数据请求还没有返回时,展示 Loading 组件。
const withLoading = Loading => WapperedComponent => {
const WithLoading = props => {
return props.data.length === 0 ? (
<Loading />
) : (
<WapperedComponent {...props} />
);
};
return WithLoading;
};
复制代码
使用:
const Loading = () => <p>loading</p>;
withLoading(Loading)(Component)
复制代码
如何处理多个 HOC 嵌套问题
如果一个组件,需要请求数据,在数据未返回时展示 loading,还需要打印 props, 那么我们需要将 withData
、withLoading
和 withLogger
组合起来。
const Loading = () => <p>loading</p>;
withData(
"https://jsonplaceholder.typicode.com/posts",
{
_limit: 10,
page: 2
}
})(
withLogger('xxx')(
withLoading(Loading)(Component)
)
)
复制代码
上面这种嵌套的方式可阅读性很差,这里用 compose
组合函数优化一下。
const compose = (...fns) => x => fns.reduceRight((x, fn) => fn(x), x);
const Loading = () => <p>loading</p>;
compose(
withData(
"https://jsonplaceholder.typicode.com/posts",
{
_limit: 10,
page: 2
}
),
withLogger("这里是xxx"),
withLoading(Loading),
)(Component);
复制代码
优化后的代码明显更为易读,当然如果你不习惯从下到上执行,你也可以写成 pipe
函数,只需要将 compose
函数中 reduceRight
方法改为 reduce
即可。
如何封装多参数的 HOC
我们注意到 withData 传入了两个参数,url 和 params,假如需要多次调用 withData
,比如:
withData(
"https://jsonplaceholder.typicode.com/posts",
{
_limit: 10,
}
)
withData(
"https://jsonplaceholder.typicode.com/posts",
{
_limit: 9,
}
)
withData(
"https://jsonplaceholder.typicode.com/posts",
{
_limit: 8,
}
)
复制代码
我们发现每次调用的 url 都相同,这里可以用柯里化函数将参数封装一下:
const curry = fn => {
const loop = (...args) => args.length === fn.length
? fn(...args)
: (...arg) => loop(...args,...arg)
return loop
}
const withPostData = curry(withData)("https://jsonplaceholder.typicode.com/posts")
withPostData({_limit: 10})
withPostData({_limit: 9})
withPostData({_limit: 8})
复制代码
同理我们还可以根据不同的 url 封装成 withUserData
、withCommentData
等等。
总结
- 通过 HOC 将一些代码逻辑封装起来,可增加代码的复用性,也方便后期维护。
- HOC + 组合函数,提升代码可阅读性。
- HOC + 柯里化,封装多参数的 HOC,进一步增加代码的复用性。