
它是什么
高阶组件是一个接收组件、返回新组件的函数。它自己不是组件:组件收 props 返回 JSX,HOC 收组件返回组件。
它不是 React 的 API,而是基于组合形成的一种模式。当前的 react.dev 上没有它的专页,能查到的经典说明在已归档的旧站:Higher-Order Components。下面几条约定都出自那一页,本文会标出来。
一个例子
function withAuth(WrappedComponent) { function WithAuth({ role, ...rest }) { if (role !== 'admin') { return <p>没有访问权限</p> } return <WrappedComponent {...rest} /> }
WithAuth.displayName = `withAuth(${WrappedComponent.displayName || WrappedComponent.name})` return WithAuth}
const ProtectedPanel = withAuth(AdminPanel)三点值得留意:它没有改 AdminPanel,只是包了一层;{...rest} 把与权限无关的 props 透传下去;displayName 让这层包装在 DevTools 里认得出名字。
四条约定
不要修改被包装的组件本身。别去动它的原型,也别给它挂方法,那是污染而不是复用。
透传不相关的 props。HOC 只取自己需要的那几个,其余原样传给被包装组件,否则外面传的 props 会凭空消失。
不要在组件的渲染函数里创建包装后的组件:
// 不行:每次渲染都是一个新组件,内部的 state 会被丢掉function Page() { const Enhanced = withAuth(AdminPanel) return <Enhanced />}
// 可以:在模块顶层创建一次const Enhanced = withAuth(AdminPanel)给返回的组件设置 displayName。默认情况下 DevTools 里只会看到一层匿名的包装。
ref 这件事在 React 19 变了
18 里 ref 是特殊 prop,HOC 包一层就会挡住它,必须用 forwardRef 显式转发:
// 18 的写法function withLog(Wrapped) { return forwardRef((props, ref) => <Wrapped {...props} ref={ref} />)}19 起 ref 就是普通 prop,HOC 只要把 props 透传,ref 自然跟着走。读旧代码时会看到 forwardRef 那层壳,它现在已标为弃用(见 useRef)。
今天更常用的替代
逻辑复用首选自定义 Hook。同一件事,两种写法对比:
// HOC:包一层才能用const UserListWithData = withData('/api/users')(UserList)
// Hook:组件内部直接调function UserList() { const { data, loading } = useData('/api/users') // ...}Hook 版没有多出来的组件层级,props 从哪来一眼可见,类型也更好推。HOC 版在 DevTools 里多一层,props 的来源要翻包装函数才知道。
所以新代码里,逻辑复用走 自定义 Hook。
HOC 仍然合适的场景有两类:读旧代码(class 时代的组件库、部分路由与状态库的老 API),以及确实要「把一个组件换成另一个组件」的地方,比如权限包装、给一批组件统一加埋点、用类组件写错误边界再包出去。
什么时候不要用
为了包一层而包一层。用 HOC 往下传数据(那是 Hook 的事)。嵌套超过两三层:每一层都可能改写 props,出问题时得一层层往回翻。
装饰器写法(@withLog)需要额外配置编译工具,新代码里不推荐用它来套 HOC。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 189 天
部分内容可能已过时