假设有一个组件 A,封装了获取用户信息的逻辑,然后你想要在另一个组件 B 中进行复用。
第一种方案:直接将组件 A 的代码复制到组件 B。这个是最简单直接的方式。如果代码量少、逻辑简单的话,能够快速的完成。但是如果代码量多、逻辑复杂的话,迁移过程会很困难。并且,这种方式还会造成代码重复,不利于后续的维护,容易出 Bug。
其他方案:Render Props,High Order Function (HOC) 或者 hooks。
Render Props
The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function.
Render Prop 就是组件通过 props 里的 render 字段来执行渲染。 比如说,我们将公用逻辑封装到某个组件 A 里。然后其他组件,例如 B,C 想要复用 A 的逻辑的时候,便通过 render 属性传一个函数给 A,由 A 来执行渲染,并且将 B,C 需要的逻辑、数据传给 B,C。
function A(props) {
const sharedLogic = () => { return 'render prop'; }
const sharedVar = sharedLogic();
return props.render(sharedVar)
}
function B(props) {
return <div>hi, {props.sharedVar}</div>
}
function C(props) {
return <div>hello, {props.sharedVar}</div>
}
export default function App() {
return (
<div className="App">
<A render={(val) => (<B sharedVar={val}/>)}/>
<A render={(val) => (<C sharedVar={val}/>)} />
</div>
);
}
HOC
Concretely, a higher-order component is a function that takes a component and returns a new component.
HOC,就是返回另一个组件的组件。所以我们可以把公用逻辑封装起来,其他需要用到的组件,就通过 HOC 来返回一个新的包含公用逻辑的组件。
function A(WrappedComponent) {
return function(props) {
const sharedLogic = () => { return 'hoc'; }
const sharedVar = sharedLogic();
return <WrappedComponent {...props} sharedVar={sharedVar} />
}
}
function B(props) {
return <div>hi, {props.sharedVar}</div>
}
function C(props) {
return <div>hello, {props.sharedVar}</div>
}
const WrappedB = A(B);
const WrappedC = A(C);
export default function App() {
return (
<div className="App">
<WrappedB />
<WrappedC />
</div>
);
}
Hooks
我们还可以写自己的 hook,将公用逻辑封装到一个自定义的 hook 里面,然后在其他组件进行复用。
import { useEffect, useState } from "react";
function useSharedVar(x) {
const [sharedVar, setSharedVar] = useState("");
useEffect(() => {
setSharedVar("custom hook");
}, []);
return sharedVar;
}
function B(props) {
const sharedVar = useSharedVar("b");
return <div>hi, {sharedVar}</div>;
}
function C(props) {
const sharedVar = useSharedVar("c");
return <div>hello, {sharedVar}</div>;
}
export default function App() {
return (
<div className="App">
<B />
<C />
</div>
);
}
总结
这三种方案都能够解决代码复用的问题。具体情况要使用哪种方案,需要具体分析。已知的一些要注意的方面就是:
- hooks 在 v16.8 版本才引进,低版本无法使用
- HOC 容易造成深层次的嵌套、可读性差、调试困难(如果没有给返回的组件命名),并且重名 props 可能会被覆盖