React 逻辑复用: Render Props, HOC 和 Hooks

1,432 阅读2分钟

假设有一个组件 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 可能会被覆盖