--- tags: [React, HOC, Render Props, Pattern, Frontend] create time: 2026-04-29 22:11 --- # HOC 与 Render Props ## 概述 Hooks 出现之前,HOC(高阶组件)和 Render Props 是 React 中复用它逻辑的两种主要模式。理解它们的原理、适用场景和局限,有助于阅读遗留代码并理解为什么 Hooks 成为更优解。 ## HOC(高阶组件) ### 概念 ```mermaid graph LR A["原始组件 Component"] -->|"注入 props"| B["HOC 函数"] B --> C["增强组件 EnhancedComponent"] C --> D["获得额外能力:日志/权限/数据"] style B fill:#F5A87D,color:#000 ``` HOC 是一个**函数**,接收组件作为参数,返回增强后的新组件: ```tsx // 基础模式 function withAuth

( WrappedComponent: React.ComponentType

) { return function WithAuth(props: P) { const user = useAuth(); if (!user) return ; return ; }; } // 使用:装饰器风格 const ProtectedPage = withAuth(function Dashboard() { return

Dashboard

; }); // TS 类型推导:保留原始 props interface Props { title: string } const StyledTitle = withTheme(function Title({ title }: Props) { return

{title}

; }); // ✅ TS 推断 Props 不变 ``` ### 常见 HOC 模式 ```tsx // 1. 日志/HUD function withLogger

(Comp: React.FC

): React.FC

{ return function LoggedComponent(props: P) { useEffect(() => console.log(`${Comp.name} mounted`), []); return ; }; } // 2. 加载态封装 function withLoading

( Comp: React.FC

, fetchData: () => Promise ): React.FC

{ return function LoadingWrapper(props: P) { const [loading, setLoading] = useState(true); useEffect(() => { fetchData().then(() => setLoading(false)); }, []); return loading ? : ; }; } // 3. Props 转换(驼峰→kebab) function withPropsTransformer

( Comp: React.FC

, transform: (p: P) => Record ): React.FC { return function Transformed(props: P) { const extra = transform(props); return ; }; } ``` ### HOC 的局限性 | 问题 | 说明 | |------|------| | **Static 属性丢失** | `withAuth(Page)` 返回的新组件没有 Page.getInitialProps | | **Wrapper Hell** | `withAuth(withLogging(withData(Page)))`——嵌套过深调试困难 | | **Props 冲突** | 多个 HOC 都注入 `data` prop,后一个覆盖前一个 | | **ref 丢失** | 直接传 ref 给增强组件会报错(需用 forwardRef 包装) | > [!tip] HOC 兼容 ref > ```tsx > export function withAuth

(Wrapped: React.ComponentType

) { > return React.forwardRef((props, ref) => { > const { authenticated } = useAuth(); > if (!authenticated) return ; > return ; > }); > } > ``` ## Render Props ### 核心思想 通过 prop 传递一个**函数**,该函数返回 JSX——将 UI 渲染逻辑委托给调用方。 ```tsx interface MouseTrackerProps { render: (position: { x: number; y: number }) => React.ReactNode; } function MouseTracker({ render }: MouseTrackerProps) { const [pos, setPos] = useState({ x: 0, y: 0 }); useEffect(() => { const handler = (e: MouseEvent) => setPos({ x: e.clientX, y: e.clientY }); window.addEventListener("mousemove", handler); return () => window.removeEventListener("mousemove", handler); }, []); // 🎯 关键:render 函数返回什么就渲染什么 return

{render(pos)}
; } // 使用 (
Cursor at ({x}, {y})
)} />; ``` ### 等价于 children 的情况 ```tsx // 当 render prop 只是把数据传给 children 时,可以用 children 替代 function DataProvider({ children }: { children: (data: Data) => React.ReactNode }) { const data = useDatabase(); return <>{children(data)}; } {(data) => } ; ``` ## HOC vs Render Props vs Custom Hook ```mermaid graph TB A["逻辑复用需求"] --> B["方案对比"] B --> C["HOC"] B --> D["Render Props"] B --> E["Custom Hook"] C --> F["⚠️ Wrapper 嵌套深"] C --> G["⚠️ 静态方法丢失"] C --> H["✅ 不修改原组件结构"] D --> I["⚠️ 回调地狱"] D --> J["⚠️ Prop 命名冲突风险"] D --> K["✅ 灵活的 UI 控制"] E --> L["✅ 简洁直观"] E --> M["✅ 可直接操作 state / effect"] E --> N["✅ 无 wrapper 嵌套"] E --> O["❌ 只能用于组件内部"] style L fill:#4FC08D,color:#fff style M fill:#4FC08D,color:#fff style N fill:#4FC08D,color:#fff ``` ## 为什么 Hooks 取代了它们? ```tsx // ❌ HOC 方式 const ConnectedUserList = withAuth(withCache(withPagination(UserList))); // 三层嵌套 → 调试困难、性能不可见、type 推导混乱 // ✅ Hook 方式 function UserList() { useAuth(); // 身份验证 const cache = useCache(); // 数据缓存 const pagination = usePagination(); // 分页管理 return
{/* ... */}
; } // 扁平可读、天然共享 state、TS 完美推断 ``` > [!note] HOC 和 Render Props 真的被淘汰了吗? > - HOC:在需要**包裹**组件但不修改其内部的场景仍有价值(如第三方库封装) > - Render Props:当父组件需要**完全控制子组件的渲染内容**时仍然有用 > - 但 90%+ 的场景,Custom Hook 是更好的选择 ## 关联笔记