React很適合構建大應用,除了它靈活的 JSX 語法外還有個很重要的緣由,就是它的高階組件和組件的反向繼承。咱們在構建大型應用中,高階組件和反向繼承讓不少業務場景很高效的實現。開發中咱們確定會將UI組件庫,從新封裝成符合要求設計的組件,供你們統一調用;以及咱們在開發過程當中也會存在UI組件庫遷移,兩個庫的組件名稱以及API名稱存在不一致狀況,這就須要咱們將要組件名稱和API名稱進行重寫了;固然最重要的一個業務場景是,咱們的項目完成度很高時,已經有不少節點調用的一個複雜組件,須要本身在它的基礎上進行一些拓展和重寫,但又存在牽一髮而動全身的風險,這時咱們的高階組件和反向繼承就發揮了巨大的做用。node
React 對高階組件的官方解釋就是高階組件是:參數爲組件,返回值爲新組件的函數。
咱們在構建應用中,用高階組件(屬性代理模式)實現了裝飾模式(瞭解下 ES7 中的裝飾器)。react
將 antd 組件庫進行重寫,使它的組件庫風格符合設計要求。
index.js 調用bash
import MyButton from 'myButton.js'
import MyTree from 'myTree.js'
export {
MyButton,
MyTree
}
複製代碼
咱們將 antd 的 Button 封裝成咱們本身的 MyButton 樣式風格。colors 相似於 antd 中的 type,特性有 primary、danger等顏色。下面只寫colors 爲 danger 的 Button。antd
import 'myButton.less'
export default class MyButton extends Component {
render() {
let { onClick, colors, className, ...others } = this.props
return (
<Button
className=`${className} my-button-wrap button-${colors}`
onClick={(e) => {
onClick(e)
}
{...others}
>
</Button>
)
}
}
複製代碼
myButton.less 部分實現app
.button-danger {
background-color: #e14c46;
color: #fff;
&:hover {
background-color: #d7332c;
color:#fff;
}
&:disabled {
background-color: #F19B96;
color: #F2F2F2 ;
&:hover {
color: #F2F2F2 ;
background-color: #fdcac6;
}
}
}
複製代碼
咱們在項目中,遇到框架遷移是會有 API 名稱不一樣的問題。咱們將 antd 中樹形控件的 API,改寫成咱們以前項目中的 API。好比,咱們將以前的 onExpand 改寫成 myExpand。固然,咱們還能夠在裏面加入更加複雜的功能,這就要依據咱們的業務須要來肯定功能了。這裏只是進行演示,因此儘可能簡單。框架
const { TreeNode } = Tree;
export default class MyTree extends Component {
render() {
let { myClick, myExpand, ...others } = this.props
return (
<Tree
onCheck={() => {
myCheck()
}
onExpand = {(expandedKeys, {expanded: bool, node}) => {
myExpand(expandedKeys, {expanded: bool, node})
}
{...others}
>
</Tree>
)
}
}
MyTree.myTreeNode = TreeNode
複製代碼
固然咱們有時寫一個業務,徹底能夠在,以前的組件的基礎上,額外進行一些修飾,就能夠知足需求,用裝飾器的方式,就能夠節省不少的代碼。這樣高階組件就起到很重要的做用。(裝飾器裝飾器是一個實驗性的 JavaScript 提案。)less
import OldComponent from "./Component.js"
const newComponent = (WrapComponent) => {
return class extends WrapComponent {
render() {
let props = this.props
let newProps = {
@click:() => {},
...this.props
}
return <WrapComponent />
}
}
}
export default newComponent(OldComponent)
複製代碼
獲得程序的需求前,將實現方法相同的部分抽出,寫入到高階函數內部。而後,函數中以不一樣的組件,以及不一樣的函數爲參數,返回功能類似但徹底不一樣的組件。函數
function hocFn({
WrappedComponent,
data,
callback = () => console.log('沒有傳入回調函數')
}) {
// ...並返回另外一個組件...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: data
};
}
componentDidMount() {
callback()
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
//建立不一樣表格()
let TreeTable = hocFn(Tree, data1, () => console.log('建立一個樹形表格'))
let EditTable = hocFn(Form, data1, () => console.log('建立一個可編輯表格'))
複製代碼
反向繼承最核心的兩個做用,一個是渲染劫持,另外一個是操做state吧。反向繼承有兩種寫法,兩種寫法存在不一樣的能力。ui
實現一個子組件 ComponentChild.jsthis
export default class ComponentChild extends Component {
constructor(props) {
super(props)
this.state = {
num: 2019
}
}
componentDidMount() {
console.log("child component Did Mount")
}
clickComponent() {
console.log("Component click")
}
render() {
return (
<div>{this.state.num}</div>
)
}
}
複製代碼
imoprt ComponentChild from './ComponentChild.js'
let iihoc = WrapComponet => class extends WrapComponet {
constructor(props) {
super(props)
this.state = {
num: 2000
}
}
componentDidMount() {
console.log('iihoc componentDidMount')
this.clickComponent()
}
return (
<div>
<div onClick={this.clickComponent}>iiHoc 點擊</div>
<div><WrapComponent /></div>
</div>
)
}
export default iihoc(ComponentChild)
複製代碼
這樣的方式比屬性代理好的是,在外部組件能夠調用被繼承組件的方法。但不能將被繼承的 state 和 鉤子覆蓋掉。
imoprt ComponentChild from './ComponentChild.js'
let iihoc = WrapComponet => class extends WrapComponet {
constructor(props) {
super(props)
this.state = {
num: 2000
}
}
componentDidMount() {
console.log('iihoc componentDidMount')
this.clickComponent()
}
return (
<div>
<div onClick={this.clickComponent}>iiHoc 點擊</div>
<div>{super.render()}</div>
</div>
)
}
export default iihoc(ComponentChild)
複製代碼
這樣的方式,外部組件的 state 能夠將,被繼承組件的 state 和 鉤子函數完全覆蓋掉。同時,外部組件也能夠調用被繼承組件的方法。
render() {
let renderchild = super.render();
let newProps = {
...renderTree.props,
onClick: this.handleClick
};
const newRenderchild = React.cloneElement(
renderchild,
newProps
);
return newRenderchild;
}
複製代碼
高階組件、render props 等其餘抽象層組成的組件會造成「嵌套地獄」。在 React16.8 中的HOOK是對嵌套地獄的一種解決方式吧。