React 逻辑复用怎么选:自定义 Hook、HOC 还是 Render Props? React 逻辑复用怎么选自定义 Hook、HOC 还是 Render Props【免费下载链接】front-end-interview-handbookFront End interview preparation materials for busy engineers (updated for 2026)项目地址: https://gitcode.com/GitHub_Trending/fr/front-end-interview-handbook当多个组件需要同一段逻辑时——比如统一的登录检查、监听鼠标位置、管理一组计数状态——React 里常见的复用手段是自定义 Hook、高阶组件HOC和 Render Props。这篇文章基于 front-end-interview-handbook 仓库中 React Interview Playbook 的 Design Patterns for React Interviews 与 React Hooks for Interviews给出三种写法的实现示例和文档中明确的选用条件帮你在动手前定下该用哪一种。先判断代码库现状决定选型的两个条件设计模式文档给出了明确的优先顺序原文 TIP在 modern React 代码中优先用自定义 Hook 而不是 HOC 或 Render Prop。Hook 在不包裹组件树的前提下共享逻辑避免了 HOC 带来的 wrapper hell 和 prop-collision 问题。只有在需要与 class-component 代码库集成时才使用 HOC。文档的面试要点一节What you need to know for interviews进一步确认HOC 和 Render Props 曾经是代码重用的流行模式但已被 Hook 大量取代它们仍然有用的场景是 Hook 不够用或不可用的地方例如仍在使用类组件的遗留代码库。据此可以按下面顺序做选择函数组件为主的现代代码库 →自定义 Hook这是默认路径需要把渲染什么交还给调用方做一个行为与外观分离的 headless 组件 →Render Props要给已有组件尤其是类组件套一层额外行为又不想改动原组件 →HOC。自定义 Hook现代代码库的默认选择Hooks 自 React 16.8 引入是函数组件中使用状态和生命周期能力的方式。Hooks 文档的 Custom hooks 一节对什么才算一个自定义 Hook给出了硬性约束是 JavaScript 函数且以use开头如useCounter、useFetch必须调用其他 React HookuseState、useEffect、useMemo等。如果这个辅助函数没有调用任何 Hook那它不需要是 Hook保留为普通函数即可use前缀是 linter 和 React 识别一个函数是否为 Hook 的依据——没有前缀rules-of-hooks linter 就无法强制调用顺序不变量保持单一用途一个 Hook 只做一件事可选地返回状态或函数供组件使用。文档示例是一个useCounter把计数状态和三个操作封装后返回import { useState } from react; function useCounter(initialValue 0) { const [count, setCount] useState(initialValue); const increment () setCount(count 1); const decrement () setCount(count - 1); const reset () setCount(initialValue); return { count, increment, decrement, reset }; } function CounterComponent() { const { count, increment, decrement, reset } useCounter(10); return ( div pCount: {count}/p button onClick{increment}/button button onClick{decrement}-/button button onClick{reset}Reset/button /div ); }这个例子同时说明了 Hook 封装的价值它不暴露setCount调用方被限制在暴露的操作范围内只能自增、自减或重置。文档还列出了使用自定义 Hook 的四个目的代码可复用、关注点分离组件专注渲染Hook 处理逻辑、组件更干净易读、副作用被封装从而更易于调试和测试。设计模式文档在 Container/presentational 一节的替代方案里也把自定义 Hook例如抓取用户数据的useUser列为更现代、可在多个组件中保持逻辑可重用的做法。自定义 Hook 内部遵循与普通 Hook 相同的规则只能在顶层调用不能放在循环、条件、嵌套函数或try/catch/finally中只能从 React 函数组件或自定义 Hook 中调用。Hook 每次渲染必须以相同顺序调用React 是按调用位置而不是名字来识别每个 Hook 的。HOC只在类组件代码库中回退使用HOC 的定义一个接收组件并返回增强组件的函数返回的组件带有额外的 props 或逻辑用于在多个组件之间复用代码。文档给出的withAuth示例通过localStorage中的 token 做鉴权包装function withAuth(Component) { return function WrappedComponent(props) { const isAuthenticated localStorage.getItem(token); // Check auth status return isAuthenticated ? Component {...props} / : pAccess Denied/p; }; } function Dashboard() { return h1Dashboard/h1; } const ProtectedDashboard withAuth(Dashboard);按文档HOC 适用于三类场景在多个组件间复用逻辑如鉴权、analytics、日志、抓取数据在不修改原始组件的前提下添加行为Hook 不可用的场景例如类组件。但文档同时给出了限制HOC 在 pre-hooks 时代更有用现在有了自定义 Hook 之后已经不再那么普及且 HOC 通过包裹组件树来注入逻辑会引入 wrapper hell 和 prop-collision 问题。所以除非你在集成类组件代码库否则不要选它。Render Props把渲染什么交给调用方Render Props 的机制把一个渲染元素的函数作为 prop 传给组件组件用某些参数通常是它自身的状态来调用这个 prop。这样父级能基于该组件的内部状态来渲染同时仍能从外部自定义行为和外观。文档的MouseTracker示例监听mousemove事件并记录位置import { useState, useEffect } from react; function MouseTracker({ render }) { const [position, setPosition] useState({ x: 0, y: 0 }); useEffect(() { const handleMouseMove (event) { setPosition({ x: event.clientX, y: event.clientY }); }; window.addEventListener(mousemove, handleMouseMove); return () window.removeEventListener(mousemove, handleMouseMove); }, []); return render(position); } function App() { return ( MouseTracker render{(position) ( p Mouse: {position.x}, {position.y} /p )} / ); }以上 import 行为运行所需补充源文档示例未列出。文档标注其适用场景在共享逻辑的同时保持 UI 灵活Hook 不可用的场景例如类组件以及提供逻辑和行为、同时允许自定义外观的 headless 组件。组件 API 设计文档User Interface Components and API Design Principles从另一侧印证了这一定位render props 是组件用来知道要渲染什么的函数 props对把行为与表示分离很有用Radix、Headless UI、Reach UI 等 behavioral/headless UI 库大量使用 render props。三种写法的对照与自检方案文档给出的适用条件机制文档指出的局限自定义 Hook函数组件为主的现代代码默认选择以use开头的函数封装状态与副作用返回状态或函数必须以use开头且调用 Hook 才成立一个 Hook 一个用途Render Props需要调用方决定渲染内容headless 组件类组件场景把渲染函数作为 prop 传入组件用自身状态调用它组件调用方必须提供渲染逻辑HOC类组件代码库给组件套一层额外行为而不改原组件函数接收组件并返回增强组件wrapper hell 与 prop-collision现代代码中不再普及写完之后的判断依据都来自文档本身的约束而不是额外约定linter 校验自定义 Hook 必须带use前缀rules-of-hooks linter 才能强制调用顺序Hook 出现在循环、条件、事件处理器或条件return之后都会破坏调用顺序不变量这是文档 Rules of hooks 一节明确列出的错误形态封装是否到位以useCounter为例调用方只能拿到count和三个操作函数拿不到setCount状态变更被限制在 Hook 定义的接口内包装行为是否符合预期文档示例行为withAuth在无 token 时渲染Access Denied有 token 时渲染原组件MouseTracker的调用方通过render回调拿到每次更新的position并在卸载时通过 cleanup 移除事件监听。面试中的口径与下一步设计模式文档给出的面试结论一句话就是use hooks firstHOC 和 Render Props 已被 Hook 大量取代只在 Hook 不够用或不可用如类组件遗留代码库时仍有用。Hooks 文档的面试要点里也把能写出并解释一个可复用自定义 Hook列为要求项。这两篇文档末尾都列了对应的 quiz 练习HOC、render props、container/presentational 各有一道以及 hooks 规则与自定义 Hook 相关题目可以按 react-design-patterns 文档 和 react-hooks 文档 中的 Practice questions 部分继续练习。【免费下载链接】front-end-interview-handbookFront End interview preparation materials for busy engineers (updated for 2026)项目地址: https://gitcode.com/GitHub_Trending/fr/front-end-interview-handbook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考