一個高階組件就是一個函數(shù)续担,這個函數(shù)接受一個組件作為輸入,然后返回一個新的組件作為結(jié)果活孩,而且物遇,返回的新組件擁有了輸入組件所不具有的功能。我們可以這么打比方憾儒,每個組件最后都返回了一個jsx询兴,而jsx實質(zhì)上一個對象,相當于我們傳入一個對象起趾,最后返回了一個新對象诗舰,它具有參數(shù)對象不具有的功能
// 刪除user這個props
function removeUserProp(WrapperComponent){
return class WrappingComponent extends React.Component{
render(){
const {user,...otherProps} = this.props
return <WrapperComponent {...otherProps}>
}
}
}
定義高階組件的意義何在呢?
首先训裆,重用代碼 有時候很多 React 組件都需要公用同樣一個邏輯眶根,比如說 react-redux中容器組件的部分,沒有必要讓每個組件都實現(xiàn)一遍 shouldComponentUpdate 這些生命周期函數(shù)边琉,把這部分邏輯提取出來属百,利用高階組件的方式應用出去,就可以減少很多組件的重復代碼
其次变姨,修改現(xiàn)有 React 組件的行為 有些現(xiàn)成 React 組件并不是開者自己開發(fā)的族扰,來自于第3方,或者定欧,即使是我們自己開發(fā)的渔呵,但是我們不想去觸碰這些組件的內(nèi)部邏輯,這時候高階組件有了用武之地 通過一個獨立于原有組件的函數(shù)砍鸠,可以產(chǎn)生新的組件扩氢,對原有組件沒有任何侵害。
根據(jù)返回的新組件和傳人組件參數(shù)的關(guān)系睦番,高階組件的實現(xiàn)方式可以分為兩大類:
代理方式的高階組件
繼承方式的高階組件
代理方式的高階組件
上面的 removeUserProp 例子就是一個代理方式的高階組件类茂,特點是返回的新組件類直接繼承自 React. Component 新組件扮演的角色是傳入?yún)?shù)組件的一個“代理”,在新組建的 render 函數(shù)中托嚣,把被包裹組件渲染出來巩检,除了高階組件自己要做的工作,其余功能全都轉(zhuǎn)手給了被包裹的組件示启。
代理方式的高階組件兢哭,可以應用在下列場景中:
操縱 prop
訪問 ref
抽取狀態(tài)
包裝組件
- 操縱 prop
// 添加新props
const addNewProp = (WrapperComponent,newProps) => {
return class WrappingComponent extends React.Component{
render(){
return <WrapperComponent {...this.props} {...newProps}>
}
}
}
- 訪問 ref
// 獲取refs
const refsHOC = (WrapperComponent) => {
return class HOCComponent extends React.Component{
constructor(){
super(...arguments)
this.linkRef = this.linkRef.bind(this)
}
linkRef(wrappedInstance){
this._root = wrappedInstance
}
render(){
const props = {...this.props,ref:this.linkRef}
return <WrapperComponent {...props}>
}
}
}
- 抽取狀態(tài)
const doNothing = () => ({})
function connect(mapStateToProps=doNothing,mapDispatchToProps=doNothing){
return function(WrapperComponent){
class HOCComponent extends React.Component{
//定義聲明周期函數(shù)
constructor(){
super(...arguments)
this.onChange = this.onChange.bind(this)
this.store = {}
}
componentDidMount(){
this.context.store.subscribe(this.onChange)
}
componentWillUnMount(){
this.context.store.unsubscribe(this.onChange)
}
onChange(){
this.setState({})
}
render(){
const store = this.context.store
const newProps = {
...this.props,
...mapStateToProps(store.getState()),
...mapDispatchToProps(store.dispatch())
}
return <WrapperComponent {...newProps}>
}
}
HOCComponent.contextTypes = {
store:React.PropTypes.object
}
return HOCComponent
}
}
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || Component
}
- 包裝組件
const styleHOC = (WrappedComponent,style) => {
return class HOCComponent extends React.Component{
render(){
return(){
<div style={style}>
<WrappedComponent {...this.props} />
</div>
}
}
}
}
繼承方式的高階組件
繼承方式的高階組件采用繼承關(guān)系關(guān)聯(lián)作為參數(shù)的組件和返回的組件,假如傳入的組件參數(shù)是 WrComponeappednt 夫嗓,那么返回的組件就直接繼承自 WrappedComponent
function removeUserProp(WrapperComponent){
//繼承于參數(shù)組件
return class NewComponent extends WrapperComponent{
render(){
const {user,...otherProps} = this.props
this.props = otherProps
//調(diào)用WrapperComponent的render方法
// 只是一個render函數(shù)迟螺,不是一整個生命周期
return super.render()
}
}
}
繼承方式的高階組件可以應用于下列場景:
操縱 prop
操縱生命周期函數(shù)
- 操縱 prop
const modifyPropsHOC = (WrappedComponent) => {
return class NewComponent extends WrappedComponent{
render(){
const elements = super.render()
const newStyle = {
color:(elements && elements.type === 'div') ? 'red' : 'green'
}
const newProps = {...this.props,style:newStyle}
return React.cloneElement(elements,newProps,elements.props.children)
}
}
}
- 操縱生命周期函數(shù):修改參數(shù)組件的生命周期
const onlyForLoggedHOC = (WrappedComponent) => {
return class NewComponent extends WrappedComponent{
render(){
if(this.props.loggedIn){
return super.render()
}else{
return null
}
}
}
}
const cacheHOC = (WrappedComponent) => {
return class NewComponent extends WrappedComponent{
shouldComponentUpdate(nextProps,nextState){
return !nextProps.userCache
}
}
}
以函數(shù)為子組件
高階組件并不是唯一可用于提高 React 組件代碼重用的方法 在上 節(jié)的介紹中可以體會到冲秽,高階組件擴展現(xiàn)有組件功能的方式主要是通過 props ,增加 props 或者減少props 矩父,或者修改原有的 props 以代理方式的高階組件為例锉桑,新產(chǎn)生的組件和原有的組件說到底是兩個組件,是父子關(guān)系窍株,而兩個 React 組件之間通信的方式自然是 props 因為每個組件都應該通過 propTypes 聲明自己所支持的 props 高階組件利用原組件的 props擴充功能民轴,在靜態(tài)代碼檢查上也占優(yōu)勢>
但是,高階組件也有缺點球订,那就是對原組件的 props 有了固化的要求 也就是說后裸,能不能把一個高階組件作用于某個組件 ,要先看一下這個組件 是不是能夠接受高階組件傳過來的 props 冒滩,如果組件 并不支持這些 props 微驶,或者對這些 props 的命名有不同,或者使用方式不是預期的方式开睡,那也就沒有辦法應用這個高階組件因苹。
“以函數(shù)為子組件”的模式就是為了克服高階組件的這種局限而生的 在這種模式下,實現(xiàn)代碼重用的不是一個函數(shù)士八,而是一個真正的 React 組件容燕,這樣的 React 組件有個特點,要求必須有子組件的存在婚度,而且這個子組件必須是一個函數(shù) 在組件實例的生命周期函數(shù)中, this props children 引用的就是子組件官卡, render 函數(shù)會直接this.props.children當做函數(shù)來調(diào)用蝗茁,得到的結(jié)果就可以作為 render 返回結(jié)果的一部分
class CountDown extends React.Component{
constructor(){
super(...arguments)
this.state = {count:this.props.startCount}
}
componentDidMount(){
this.intervalHandle = setInterval(() => {
const newCount = this.state.count - 1
if(newCount >= 0){
this.setState({count:newCount})
}else{
window.clearInterval(this.intervalHandle)
}
},1000)
}
componentWillUnMount(){
if(this.intervalHandle){
window.clearInterval(this.intervalHandle)
}
}
render(){
return this.props.children(this.state.count)
}
}
CountDown.propTypes = {
children:PropTypes.func.isRequired,
startCount:PropTypes.number.isRequired
}
<CountDown>
{
(count) => <div>count</div>
}
</CountDown>
<CountDown>
{
(count) => <div>{count > 0 ? count : 'happy new year'}</div>
}
</CountDown>
<CountDown>
{
(count) => <Bomb countdown={count}>
}
</CountDown>
以函數(shù)為子組件這種方法非常合適做動畫,作為子組件的函數(shù)主要專注于參數(shù)來渲染就可以了寻咒;但是它難以做性能優(yōu)化哮翘,因為子組件是函數(shù),沒有生命周期毛秘,無法利用shouldComponentUpdate