HOC、Render Props 和自定义 Hooks 有什么区别?逻辑复用该怎么选?
先理解原理和示例,再练习完整口述与追问。
回答要点
HOC 是接收组件、返回增强组件的函数;Render Props 通过函数 prop 把数据交给调用方渲染;自定义 Hook 在函数组件中直接复用有状态逻辑。HOC 和 Render Props 会带来嵌套地狱、props 命名冲突和来源不清,新代码优先用自定义 Hooks;HOC 仍适合在组件外层统一包裹,例如权限、埋点。
原理拆解
HOC 如 withRouter(Component)、connect(mapState)(Component),多个 HOC 叠加后,组件树中出现多层包装,props 从哪层注入难以追踪,同名 props 会被覆盖;还需要转发 ref、复制静态方法、设置 displayName。Render Props 如 <Mouse>{pos => <Cursor {...pos} />}</Mouse> 依赖关系明确,但层数多了形成回调嵌套。
自定义 Hook 只是以 use 开头、调用其他 Hook 的函数,每个调用者拥有独立的状态。它没有额外的组件层级,返回值命名由调用方决定,也能组合。限制是只能在函数组件顶层调用,不能写在条件或循环中。
示例与推演
function useOnline() {
const [online, setOnline] = useState(() => navigator.onLine)
useEffect(() => {
const update = () => setOnline(navigator.onLine)
window.addEventListener('online', update)
window.addEventListener('offline', update)
return () => {
window.removeEventListener('online', update)
window.removeEventListener('offline', update)
}
}, [])
return online
}
const withAuth = Component => props => (useAuth().user ? <Component {...props} /> : <Navigate to="/login" />)
易错点
在 render 中调用 HOC 会每次生成新组件类型,导致子树卸载重建、状态丢失;Hook 共享的是逻辑而不是状态,两个组件调用同一个 Hook 得到两份状态。
参考回答可直接练习口述
HOC 是包装组件的函数,Render Props 是用函数 prop 把渲染权交给调用方,两者都能复用逻辑,但会带来层层嵌套、props 来源不清和命名冲突。自定义 Hook 直接在组件里调用,没有额外层级,返回值由调用方命名,组合也方便,所以新代码我优先写 Hook。HOC 仍适合路由守卫、埋点这种在外层统一包裹的场景,注意不要在 render 里创建 HOC。