更快助你弄懂React-高階組件

談到react,咱們第一個想到的應該是組件,在react的眼中可真的是萬物皆組件。就連咱們獲取數據用到的axios也能夠用組件來表示...好比,咱們能夠這樣封裝react

<Request
  instance={axios.create({})} /* custom instance of axios - optional */
  method="" /* get, delete, head, post, put and patch - required */
  url="" /*  url endpoint to be requested - required */
  data={} /* post data - optional */
  params={} /* queryString data - optional */
  config={} /* axios config - optional */
  debounce={200} /* minimum time between requests events - optional */
  debounceImmediate={true} /* make the request on the beginning or trailing end of debounce - optional */
  isReady={true} /* can make the axios request - optional */
  onSuccess={(response)=>{}} /* called on success of axios request - optional */
  onLoading={()=>{}} /* called on start of axios request - optional */
  onError=(error)=>{} /* called on error of axios request - optional */
/>

在項目中咱們能夠這樣寫ios

import { AxiosProvider, Request, Get, Delete, Head, Post, Put, Patch, withAxios } from 'react-axios'
...
render() {
  return (
    <div>
      <Get url="/api/user" params={{id: "12345"}}>
        {(error, response, isLoading, makeRequest, axios) => {
          if(error) {
            return (<div>Something bad happened: {error.message} <button onClick={() => makeRequest({ params: { reload: true } })}>Retry</button></div>)
          }
          else if(isLoading) {
            return (<div>Loading...</div>)
          }
          else if(response !== null) {
            return (<div>{response.data.message} <button onClick={() => makeRequest({ params: { refresh: true } })}>Refresh</button></div>)
          }
          return (<div>Default message before request is made.</div>)
        }}
      </Get>
    </div>
  )
}

有點過度了...至少我是以爲仍是要根據我的的代碼習慣來吧,若是全部組件都是這麼處理請求的,包括一些簡單的get請求,我以爲真的沒這個必要,而且咱們的一些通用API也不太好統一管理redux

那麼,高階組件究竟是什麼?axios

a higher-order component is a function that takes a component and returns a new component.

右鍵翻譯 ------> 高階組件就是一個函數,且該函數接受一個組件做爲參數,並返回一個新的組件。
嗯,看起來就是這麼簡單,其實用起來也是api

一、具體來講一下,咱們先用高階函數來舉個例子,一個 showUserPermit, 一個showUserVipInfo,兩個函數先從localStorage讀取了userVIP,以後針對userVIP作了一些處理。antd

function showUserPermit() {
   let vip = localStorage.getItem('u_V');
   console.log(`您能夠享受的${u_V}的特權...`);
}

function showUserVipInfo() {
   let vip = localStorage.getItem('u_V');
   console.log(`您當前VIP等級爲${u_V},升級馬上...`);
}

showUserPermit();
showUserVipInfo();

二、咱們發現了兩個API中有兩個徹底同樣的代碼,很冗餘,這樣很差,咱們改一下吧app

function showUserPermit(u_V) {
   console.log(`您能夠享受的${u_V}的特權...`);
}

function showUserVipInfo(u_V) {
   console.log(`您當前VIP等級爲${u_V},升級馬上...`);
}

三、這樣寫看上去確實簡單了一些,可是這兩個API要想保證功能徹底必須依賴參數u_V,全部在調用這兩個函數以前咱們都必需要拿到這個參數,這樣未免有點耦合性,咱們再次改造
function showUserPermit(u_V) {echarts

console.log(`您能夠享受的${u_V}的特權...`);
}

function showUserVipInfo(u_V) {
   console.log(`您當前VIP等級爲${u_V},升級馬上...`);
}

function wrapU_V(wrappedFunc) {
    let newFunc = () => {
        let vip = localStorage.getItem('u_V');
        wrappedFunc(vip);
};
    return newFunc;
}

module.exports = { 
    showUserPermit: wrapU_V(showUserPermit), 
    showUserVipInfo: wrapU_V(showUserVipInfo)
}

四、wrapU_V就是一個沒有任何反作用的高階函數,那麼他的意義是什麼?又作了什麼?它幫咱們處理了u_V,而且調用了目標函數(函數參數),這樣當你再次使用導出的showUserPermit的時候根本沒必要要去關心u_V高低是怎麼來的,到底需求什麼外部條件,你只要知道它能幫我實現我想要作的事情就能夠了!同時省去了每一次調用前都先要看一下它的參數是什麼?怎麼來?甚至根本不用關心wrapU_V內部是如何實現的,Array.map,setTimeout均可以稱爲高階函數ide

高階組件
高階組件就是一個沒有反作用的純函數,對就是一個函數
咱們將上面的例子用component來重構一下函數

import React, {Component} from 'react'
...

class showUserPermit extends Component {
    constructor(props) {
        super(props);
        this.state = {
            VIP: ''
        }
    }

    componentWillMount() {
        let VIP = localStorage.getItem('u_V');
        this.setState({
            VIP
        })
    }

    render() {
        return (
            <div>showUserPermit... {this.state.VIP}</div>
        )
    }
}

export default showUserPermit;

/* - */

import React, {Component} from 'react'
...

class showUserVipInfo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            VIP: ''
        }
    }

    componentWillMount() {
        let VIP = localStorage.getItem('u_V');
        this.setState({
            VIP
        })
    }

    render() {
        return (
            <div>showUserVipInfo... {this.state.VIP}</div>
        )
    }
}

export default showUserVipInfo;

剛纔發現的問題均可以映射在這兩個組件裏了
按照上面的思路咱們作一個處理

import React, {Component} from 'react'

module.exports = Wrap: (WrappedComponent) => {
    class reComponent extends Component {
        constructor() {
            super();
            this.state = {
                VIP: ''
            }
        }

        componentWillMount() {
            let VIP = localStorage.getItem('u_V');
            this.setState({
                VIP
            })
        }

        render() {
            return <WrappedComponent VIP={this.state.VIP}/>
        }
    }

    return reComponent
}

再來簡化一下 showUserVipInfo和showUserPermit組件

import React, {Component} from 'react';
import {Wrap} as templete from 'wrapWithUsername';

class showUserPermit extends Component {

    render() {
        return (
            <div>showUserPermit {this.props.username}</div>
        )
    }
}

showUserPermit = templete(showUserPermit);

export default showUserPermit;  

/*--*/
import React, {Component} from 'react';
import {Wrap} as templete from 'wrapWithUsername';

class showUserVipInfo extends Component {

    render() {
        return (
            <div>showUserVipInfo {this.props.username}</div>
        )
    }
}

showUserPermit = templete(showUserPermit);

export default showUserVipInfo;

而且高階組件中能夠分佈多個目標組件,舉一個咱們項目中的例子

圖片描述
這裏面右上角的時間選擇組件以及echarts組件是兩種不一樣身份特有的一些行爲和樣式,其它的徹底是同樣的,包括state以及共用方法都如出一轍。上代碼

render() {
  return (
    <div className="mk-genData home-module-common">
      <div className="module-header">
        <div className="module-title">...</div>
        <**GenTimerComponent** receiveTimeChange={this.getData.bind(this)}/>
      </div>
      <div className="genData-nav">
        ...
      </div>
      <div>
        <**EchartsComponent** chartData={this.state.chartData}/>
      </div>
    </div>
  )

其中GenTimerComponent,和EchartsComponent都是目標組件,咱們這樣導出

圖片描述

豁然開朗了吧,其實就是把兩個組件相同的地方或者均可能用到的地方抽離出來,說句題外話,其實原本是'高階組件'嵌套了目標組件,可是從新生成的新組建反卻是繼承了目標組件,看起來是一種控制反轉,和Vue中的extend+minix也比較像,經過繼承目標組件,除了一些靜態方法,包括生命週期,state,fun,咱們均可獲得
如今理解react-redux的connect函數~
把redux的state和action建立函數,經過props注入給了Component。
你在目標組件Component裏面能夠直接用this.props去調用redux state和action建立函數了。

ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component);

等價於

// connect是一個返回函數的函數(就是個高階函數)
const enhance = connect(mapStateToProps, mapDispatchToProps);
// 返回的函數就是一個高階組件,該高階組件返回一個與Redux store
// 關聯起來的新組件
const ConnectedComment = enhance(Component);

antd的Form也是同樣的

const WrappedNormalLoginForm = Form.create()(NormalLoginForm);

總結一下: 高階組件是對React代碼進行更高層次重構的好方法,若是你想精簡你的state和生命週期方法,那麼高階組件能夠幫助你提取出可重用的函數。通常來講高階組件能完成的用組件嵌套+繼承也能夠,用嵌套+繼承的方式理解起來其實更容易一點,特別是去重構一個複雜的組件時,經過這種方式每每更快,拆分起來更容易。至於到底用哪一個最佳還要具體看業務場景,歡迎交流探討

做者:易企秀——Yxaw
相關文章
相關標籤/搜索