HOC簡介
HOC全稱為High Order Component即高階組件, 其使用姿勢通常有兩種:
屬性代理(Props Proxy)
- 操作props
- 抽象state
- 獲取ref
- 用其他組件包裹(功能集成/樣式包裹)
簡而言之: 修改傳入組件的props.
使用姿勢常常是這樣的:
const ppHoc = WrappedComponent => class extends React.Component {
render() {
// 此處可以修改props,或者注入state,
// 總之對WrappedComponent而言就是修改了props
return <WrappedComponent {...this.props} />
}
}
反向繼承(Inheritance Inversion)
- 渲染劫持 render hijacking
- 操縱state
使用姿勢常常是這樣的:
const iiHoc = WrappedComponent => class extends WrappedComponent{
render() {
const elementTree = super.render();
const { props } = elementTree;
// 可以修改props
const newProps = {aa: 1};
return React.cloneElement(elementTree, {...props, ...newProps}, elementsTree.props.children)
}
}
繼承該組件,可基于其elementTree進行修改。能力更強,但風險也更高。
不能保證完整的子組件樹被解析, 以及靜態(tài)方法不會被繼承。
實踐
需求簡介:
目前頁面中已有多個圖表組件(單一維度的數(shù)據(jù)統(tǒng)計折線圖),目前想為每個圖表添加checkBox,可以交叉其他維度進行統(tǒng)計。

需求分析:
目前業(yè)務(wù)中每個圖表是一個封裝好的組件,(如圖一所示,標題一行和圖表是一體的,為包裝好的Chart組件)。現(xiàn)在業(yè)務(wù)中要為每個圖表都加一個CheckBox。即,需要將每個圖表組件進行再次包裝,將check state與chart組合成一個Component.
如果checkbox位置如圖2所示,則checkBox可以作為圖表組件的children,也可以作為兄弟組件,只要調(diào)整下其位置即可。
倘若checkBox要如圖3,放在title和圖表中間,則需要將CheckBox作為Chart的children才能插入到該位置,否則是沒有空間放checkbox。如何才能以較低成本,給十來個Chart組件都添加CheckBox這個Children呢?此時就只能通過Hoc修改其props.children來實現(xiàn)了
按照上圖所示布局,我們通過兩種HOC方式都來實踐下:
實踐1: 屬性代理
組件結(jié)構(gòu)如下,state保存在Parent中,CheckBox和Chart是兄弟組件。當isChecked切換狀態(tài)時,修改Chart對應(yīng)的props.
Parent
CheckBox
Chart
主要代碼如下
const interHoc = WrappedComponnet =>
class extends React.Component {
state = {
isChecked: false,
};
render() {
const { isChecked } = this.state;
let chartProps = { ...this.props };
// 修改props
const {
formatParams: { dims = [] },
} = chartProps;
const GENDER_TYPE = 'predicted_gender';
if (isChecked && !dims.includes(GENDER_TYPE)) {
chartProps.formatParams.dims = [GENDER_TYPE].concat(dims);
} else {
chartProps.formatParams.dims = dims.filter(d => d !== GENDER_TYPE);
}
return (
<div>
<CheckBox
checked={isChecked}
onChange={e => this.setState({ isChecked: e.target.value })}
>
交叉性別維度
</CheckBox>
<WrappedComponnet {...chartProps} />
</div>
);
}
};
此處是通過包裹另外組件實現(xiàn)的,也可以直接修改props.chilren = YourComponent實現(xiàn)。
實踐2:渲染劫持
通過繼承WrappedComponent,獲取其elementTree, 根據(jù)原props中的參數(shù),符合條件的,對其props和props.children進行修改。
通過繼承可以直接修改elementTree(修改其props和children)顯然能力范圍是更強大的,但風險也更高,能不用就不用吧。
const interHoc = WrappedComponent =>
class extends WrappedComponent {
state = {
isChecked: false,
};
render() {
const { isChecked } = this.state;
const elementTree = super.render();
const interCom = (
<Checkbox
checked={isChecked}
onChange={e => this.setState({ isChecked: e.target.checked })}
>
交叉性別維度
</Checkbox>
);
// 修改props
const {
props: {
formatParams: { dims = [] },
},
} = elementTree;
const GENDER_TYPE = 'predicted_gender';
elementTree.props.children = interCom;
if (isChecked && !dims.includes(GENDER_TYPE)) {
elementTree.props.formatParams.dims = [GENDER_TYPE].concat(dims);
} else {
elementTree.props.formatParams.dims = dims.filter(
i => i !== GENDER_TYPE,
);
}
return elementTree;
}
};