什么是 HOC
What is HOC
一句话:高阶组件 ——接收一个组件、返回一个增强后的新组件的函数。它是「复用组件逻辑」的老方案。
withRouter、connect(Redux)、withStyles 都是 HOC。 本质就是 #292 的高阶函数用在组件上。
三个必须注意的点(考点在这):
- 要透传 props——
<Comp {...props} />, 不然把原来的 props 吞了。 - 要拷贝静态方法—— 包一层之后原组件的静态属性丢了 (
hoist-non-react-statics干这事)。 - ref 传不进去—— 要用
forwardRef。
为什么现在少用了(这才是重点):
- wrapper 地狱—— 叠三四层之后 DevTools 里全是嵌套, 难调试。
- props 来源不明—— 组件里出现一个
userprop, 你不知道是哪个 HOC 注进来的。 - 命名冲突—— 两个 HOC 都注入
data就打架了。
自定义 hook 解决了全部三条: 平铺、来源显式 (const user = useUser()一眼看出来)、 名字由你决定。所以现在优先写 hook。
会追问:「那 HOC 还有用吗?」—— 有两个 hook 替代不了的场合:需要「包裹」渲染结果(比如给所有页面套一层错误边界或布局)、 以及要改写 props 后再传给一个你无法修改的组件。
In one line: a higher-order component — a function that takes a component and returns an enhanced one. It is the old answer to reusing component logic.
withRouter, connect (Redux) and withStyles are all HOCs. It is nothing more than the higher-order function from #292 applied to components.
Three things you must get right — the marks are here:
- Forward the props —
<Comp {...props} />, or you swallow the ones the component already had. - Copy the statics — wrapping loses the original component’s static properties (
hoist-non-react-staticsexists for this). - Refs do not pass through — you need
forwardRef.
Why it fell out of favour — this is the real point:
- Wrapper hell — stack three or four and DevTools is nothing but nesting; debugging hurts.
- Props of unknown origin — a
userprop shows up in the component and you cannot tell which HOC injected it. - Name collisions — two HOCs both injecting
datafight each other.
Custom hooks fix all three: they stay flat, the origin is explicit (const user = useUser() says it out loud), and you choose the name. So hooks come first now.
Follow-up: “Is there still a use for HOCs?” — two places hooks cannot cover: when you need to wrap the rendered output (putting an error boundary or a layout around every page), and when you have to rewrite props before handing them to a component you cannot modify.