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 的函数,每个调用者拥有独立的状态。它没有额外的组件层级,返回值命名由调用方决定,也能组合。限制是只能在函数组件顶层调用,不能写在条件或循环中。

jsx
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。