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è)務中每個圖表是一個封裝好的組件,(如圖一所示琐簇,標題一行和圖表是一體的蒸健,為包裝好的Chart組件)。現(xiàn)在業(yè)務中要為每個圖表都加一個CheckBox。即纵装,需要將每個圖表組件進行再次包裝征讲,將check state與chart組合成一個Component.
如果checkbox位置如圖2所示,則checkBox可以作為圖表組件的children橡娄,也可以作為兄弟組件诗箍,只要調整下其位置即可。
倘若checkBox要如圖3挽唉,放在title和圖表中間滤祖,則需要將CheckBox作為Chart的children才能插入到該位置,否則是沒有空間放checkbox瓶籽。如何才能以較低成本匠童,給十來個Chart組件都添加CheckBox這個Children呢?此時就只能通過Hoc修改其props.children來實現(xiàn)了
按照上圖所示布局塑顺,我們通過兩種HOC方式都來實踐下:
實踐1: 屬性代理
組件結構如下汤求,state保存在Parent中,CheckBox和Chart是兄弟組件严拒。當isChecked切換狀態(tài)時扬绪,修改Chart對應的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;
}
};