组件复用:HOC、Render Props、自定义Hook 对比

在 React 中,组件逻辑复用经历了三个主要阶段:

  1. HOC(Higher Order Component,高阶组件)
  2. Render Props(渲染属性)
  3. Custom Hook(自定义 Hook)

目前 React 官方推荐使用 Custom Hook 进行逻辑复用,但面试中经常会让你比较三者的区别。


一、HOC(Higher Order Component)

定义

高阶组件本质上是一个函数:

javascript 复制代码
const withLoading = (WrappedComponent) => {
  return (props) => {
    const [loading] = useState(false);

    if (loading) {
      return <div>Loading...</div>;
    }

    return <WrappedComponent {...props} />;
  };
};

使用:

javascript 复制代码
const UserList = () => {
  return <div>用户列表</div>;
};

export default withLoading(UserList);

原理

scss 复制代码
Component
    ↓
withXXX(Component)
    ↓
NewComponent

本质:

css 复制代码
A => B

输入一个组件,返回一个增强后的组件。


常见场景

权限校验

javascript 复制代码
const withAuth = (Component) => {
  return (props) => {
    const token = localStorage.getItem('token');

    if (!token) {
      return <div>请登录</div>;
    }

    return <Component {...props} />;
  };
};

日志埋点

javascript 复制代码
const withLog = (Component) => {
  return (props) => {
    console.log('页面访问');

    return <Component {...props} />;
  };
};

优点

逻辑复用

scss 复制代码
withAuth(User)
withAuth(Order)
withAuth(Product)

对原组件无侵入

arduino 复制代码
export default withAuth(User);

User 不需要修改代码。


缺点

1. HOC 地狱

arduino 复制代码
export default withAuth(
  withLoading(
    withLog(
      withTheme(User)
    )
  )
);

嵌套过深。


2. Props 命名冲突

ini 复制代码
<Component data={data} />

如果组件本身也有 data 属性:

ini 复制代码
<User data="xxx" />

容易覆盖。


3. 调试困难

React DevTools:

scss 复制代码
withAuth(
  withTheme(
    User
  )
)

组件树很难看。


二、Render Props

定义

通过函数作为 props 传递逻辑。

ini 复制代码
<DataProvider
  render={(data) => (
    <UserList data={data} />
  )}
/>

示例

数据提供组件

scala 复制代码
class Mouse extends React.Component {
  state = {
    x: 0,
    y: 0
  };

  componentDidMount() {
    window.addEventListener('mousemove', this.handleMove);
  }

  handleMove = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    });
  };

  render() {
    return this.props.render(this.state);
  }
}

使用:

javascript 复制代码
<Mouse
  render={({ x, y }) => (
    <h1>
      {x}, {y}
    </h1>
  )}
/>

children 也是 Render Props

javascript 复制代码
<Mouse>
  {({ x, y }) => (
    <h1>{x},{y}</h1>
  )}
</Mouse>

组件:

kotlin 复制代码
return this.props.children(this.state);

优点

灵活

调用方完全控制 UI:

ini 复制代码
<DataProvider
  render={(data) => (
    <CustomTable data={data} />
  )}
/>

不会产生 HOC 嵌套

xml 复制代码
<DataProvider />

结构更清晰。


缺点

JSX 嵌套严重

javascript 复制代码
<DataProvider>
  {(data) => (
    <ThemeProvider>
      {(theme) => (
        <User />
      )}
    </ThemeProvider>
  )}
</DataProvider>

俗称:

复制代码
Callback Hell

性能问题

每次 render:

ini 复制代码
render={(data)=>{}}

都会创建新的函数。


三、自定义 Hook(Custom Hook)

React 16.8 之后最主流方案。


示例

封装鼠标位置

scss 复制代码
function useMouse() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });

  useEffect(() => {
    const move = (e) => {
      setPosition({
        x: e.clientX,
        y: e.clientY
      });
    };

    window.addEventListener('mousemove', move);

    return () => {
      window.removeEventListener('mousemove', move);
    };
  }, []);

  return position;
}

使用

javascript 复制代码
function App() {
  const { x, y } = useMouse();

  return (
    <h1>
      {x} - {y}
    </h1>
  );
}

多组件共享逻辑

scss 复制代码
function User() {
  const { data } = useUser();
}

function Order() {
  const { data } = useUser();
}

优点

1. 代码最简洁

ini 复制代码
const data = useUser();

没有嵌套。


2. 逻辑聚合

go 复制代码
const {
  data,
  loading,
  error
} = useFetch();

逻辑放一起。


3. TypeScript 友好

arduino 复制代码
function useUser(): UserInfo {}

类型推导自然。


4. 不增加组件层级

React 树:

sql 复制代码
App
 └ User

不会出现:

scss 复制代码
withAuth(User)

或者:

markdown 复制代码
Provider
 └ render
    └ User

缺点

只能用于函数组件

scala 复制代码
class User extends React.Component {}

不能直接使用:

scss 复制代码
useUser()

必须遵守 Hook 规则

错误:

scss 复制代码
if (visible) {
  useUser();
}

正确:

ini 复制代码
const user = useUser();

if (visible) {}

四、三者对比

对比项 HOC Render Props Custom Hook
出现时间 React 早期 React 16 前后 React 16.8+
本质 组件包装组件 函数作为 props Hook 抽离逻辑
增加组件层级
JSX 嵌套
逻辑复用 最好
TypeScript 支持 一般 一般 很好
调试体验 一般
性能 一般 一般
推荐程度 ⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐

五、面试回答(标准版)

如果面试官问:

React 中 HOC、Render Props、自定义 Hook 有什么区别?

可以回答:

三者本质上都是为了解决组件间逻辑复用问题。

  • HOC 是通过包装组件返回增强组件来复用逻辑,适用于权限控制、埋点统计等场景,但容易出现组件嵌套和 props 冲突问题。
  • Render Props 是通过函数作为 props 传递共享逻辑,灵活性较高,但容易造成 JSX 嵌套过深。
  • Custom Hook 是 React Hooks 推出后的推荐方案,通过抽离状态逻辑实现复用,不增加组件层级,代码更简洁、类型支持更好,也是当前 React 项目中最常用的方案。

在现代 React 开发中,优先使用 Custom Hook;维护老项目时仍然会遇到 HOC 和 Render Props。

相关推荐
kyriewen7 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
IT_陈寒7 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
小林攻城狮8 小时前
使用 Transport 节流解决 Vercel AI SDK 流式渲染卡死问题
前端·react.js
前端缘梦8 小时前
告别 TS 运行时类型漏洞!Zod 完整入门实战教程(前端 / 全栈必备)
前端·react.js·全栈
the_answer9 小时前
Webpack vs Vite 深度对比分析
前端·webpack
转转技术团队9 小时前
验证码识别实战:前端不写页面,改训模型了?
前端
MomentYY9 小时前
Temperature:AI 的“脑洞旋钮”
前端·llm·ai编程
远航_9 小时前
OpenSpec 完整详细介绍
前端·后端
召钱熏9 小时前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
SkyWalking中文站9 小时前
认识 Horizon UI · 1/17:SkyWalking 新一代可观测性控制台
运维·前端·监控