談到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