React 中实现组件复用的方法主要有三个,高阶组件,渲染属性,还有 React Hook,下面通过一个比较常见的复用场景,数据请求的场景,分别用这三种方法实现一次
准备工作
首先定义一些公共的属性和接口
// 定义请求接口的接口
export interface Result<T> {
data: T;
loading: boolean;
hasData: boolean;
hasError: boolean;
}
// 定义 http 请求参数的信息
export interface UrlInfo {
url: string;
path: string;
method: string;
query: { [prop: string]: string };
body: any;
}
高阶组件
关于高阶组件,React 文档是这么提到的:
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
看起来很隐晦,可以联想下相似的概念,高阶函数,高阶函数指的是接受函数作为参数,或者返回值是函数的称之为高阶函数;高阶函数也是类似,高阶组件是参数为组件,返回值为新组件的函数
一个简单的请求高阶函数如下,需要传入请求的参数,还有一个子组件,往子组件里面注入组件自身的状态,就是请求的结果,还有外部传入的属性,作为子组件的属性
export default function highOrderRequest(requestOption) {
return function (Component) {
return class WapperedComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
loading: false,
hasData: false,
hasError: false,
};
}
async componentDidMount() {
this.setState({ loading: true });
/** 请求相关代码,不写了 */
await request();
this.setState({
data: {
/** 请求回来的数据 */
},
loading: false,
hasData: true,
hasError: false,
});
}
render() {
const combinedProps = { ...this.props, ...this.state };
return <Component {...combinedProps} />;
}
};
};
}
方法的使用
function UseHighOrderExample(props) {
console.log(props);
const { data, loading, hasData, hasError } = props;
return <div>high render</div>;
}
export default highOrderRequest({
url: '',
path: '/',
method: 'post',
body: { test: 1 },
})(UseHighOrderExample);
渲染属性
下面看下渲染属性,React 的文档如下:
术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术
依旧很隐晦,其实可以理解为,组件的状态作为子组件的属性,传给子组件,同样类似的概念有回调函数,那渲染函数可以称之为回调组件
一个简单的请求的渲染属性实现方式
export default class RenderProps extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
loading: false,
hasData: false,
hasError: false,
};
}
async componentDidMount() {
this.setState({ loading: true });
/** 请求相关代码,不写了 */
await request();
this.setState({
data: {
/** 请求回来的数据 */
},
loading: false,
hasData: true,
hasError: false,
});
}
render() {
return this.props.children(this.state);
}
}
可以看到实际上渲染属性是往子组件注入了这个组件的 state,这样的话,子组件就可以获取父组件的 state
使用方式
export default function UseRenderProps(props) {
return (
<RenderProps {...props}>
{(propsFromRenderProps) => {
const { data, loading, hasData, hasError } = propsFromRenderProps;
console.log(propsFromRenderProps);
return <div>render props</div>;
}}
</RenderProps>
);
}
React Hook
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。
里面提到通过自定义 Hook,实现复用
一个简单的请求的自定义 Hook
export default function useRequest(requestOptions) {
const [result, setResult] = useState({
data: null,
loading: false,
hasData: false,
hasError: false,
});
useEffect(() => {
(async () => {
setResult({
data: null,
loading: true,
hasData: false,
hasError: false,
});
await request();
setResult({
data: {
/** 请求回来的数据 */
},
loading: false,
hasData: true,
hasError: false,
});
})();
}, []);
return result;
}
如何使用
export default function UseRequestEg(requestOptions) {
const { data, loading, hasData, hasError } = useRequest(requestOptions);
console.log({ hook: 1, data, loading, hasData, hasError });
return <div>use hook</div>;
}
总结
通过高阶函数,渲染属性,hook 都可以实现代码复用,实现的方法大同小异;在自定义 Hook 大放异彩的时候,也请不要忘记高阶函数,渲染属性这些可以复用逻辑的方法。