这是我参与「第四届青训营」笔记创作活动的第 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 不会被传递