React 高阶组件(HOC) | 青训营笔记

129 阅读2分钟

这是我参与「第四届青训营」笔记创作活动的第 15 天

之前在项目中看到过这种写法

class TComponentA extends React.Component {
  // xxx
}

export const ComponentA = connect(mapStateToProps, mapDispachToProps)(TComponentA)

对于这种写法,当时并没有深究,只当它是 Redux connect 的用法死记硬背了。其实这里的 connect 是一个高阶组件(HOC)。那么什么是高阶组件呢?

概念

高阶组件(HOC)自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

高阶组件是参数为组件,返回值为新组件的函数。 简言之,就是接收一个组件并返回一个新组件。

使用场景:多个组件相同逻辑复用

比如下列例子,ChildA 和 ChildB 功能一样,都是在点击 Parent count+1 按钮时展示当前 count 值

const ChildA = (props) => {
  const [console, setConsole] = useState('')
  useEffect(() => {
    setConsole(`ChildA rendered ${props.count}`)
  }, [props.count])
  return <div>{console}</div>
}
const ChildB = (props) => {
  const [text, setText] = useState('')
  useEffect(() => {
    setText(`ChildB rendered ${props.count}`)
  }, [props.count])
  return <div>{text}</div>
}
const Parent = function () {
  const [count, setCount] = useState(1)
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>count + 1</button>
      <ChildA count={count} />
      <ChildB count={count} />
    </div>
  )
}

我们把公用的部分抽出来,就变成了

const TChild = function (props) {
  return <div>{props.name} rendered {props.count}</div>
}

const ChildA = classLog(TChild, 'ChildA')
const ChildB = classLog(TChild, 'ChildB')

const Parent = function () {
  const [count, setCount] = useState(1)
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>count + 1</button>
      <ChildA count={count} />
      <ChildB count={count} />
    </div>
  )
}

使用注意事项

优点

  • HOC支持ES6,比mixins的更优解决方案
  • HOC复用性强,将公用逻辑抽离,共享
  • HOC是纯函数,支持传入参数,增加了其扩展性
  • HOC依赖传入的props,相应状态在父组件维护

缺点

  • 组件之间复用状态逻辑很难,本质是将复用逻辑提升到父组件中,容易产生嵌套地狱
  • 由于所有抽象逻辑都被其他 React 组件所隐藏,应用变成了一棵没有可读性的组件树。而那些可见的组件也很难在浏览器的DOM中进行跟踪

其他事项

  • 不要在 render 方法中使用 HOC。因为每次 render 的时候,render 内定义的变量会被重新赋值,这会导致状态的丢失。
  • 务必复制静态方法(使用 static 生命,或使用 Component.method,可以将静态方法与 Component 一起导出)
  • Refs 不会被传递

参考:
【译】三分钟掌握 React 高阶组件
高阶组件
浅谈React 高阶组件
Hoc 与 Hooks对比以及详解