React之高階組件與反向繼承

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 和 鉤子覆蓋掉。

調用 super.render()

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;
}
複製代碼

解決嵌套地獄(HOOK)

高階組件、render props 等其餘抽象層組成的組件會造成「嵌套地獄」。在 React16.8 中的HOOK是對嵌套地獄的一種解決方式吧。

相關文章
相關標籤/搜索