摘要:之所以稱之為高階,是因?yàn)樵谥校@種嵌套關(guān)系會反映到組件樹上,層層嵌套就好像高階函數(shù)的一樣,如圖從圖上也可以看出,組件樹雖然嵌套了多層,但是實(shí)際渲染的結(jié)構(gòu)并沒有改變。你可能已經(jīng)注意到,目前我寫的所有高階函數(shù),都是形如表示為。
什么是高階組件
Higher-Order Components (HOCs) are JavaScript functions which add functionality to existing component classes.
通過函數(shù)向現(xiàn)有組件類添加邏輯,就是高階組件。
讓我們先來看一個(gè)可能是史上最無聊的高階組件:
function noId() { return function(Comp) { return class NoID extends Component { render() { const {id, ...others} = this.props; return () } } } } const WithoutID = noId()(Comp);
這個(gè)例子向我們展示了高階組件的工作方式:通過函數(shù)和閉包,改變已有組件的行為——這里是忽略id屬性——而完全不需要修改任何代碼。
之所以稱之為高階,是因?yàn)樵赗eact中,這種嵌套關(guān)系會反映到組件樹上,層層嵌套就好像高階函數(shù)的function in function一樣,如圖:
從圖上也可以看出,組件樹雖然嵌套了多層,但是實(shí)際渲染的DOM結(jié)構(gòu)并沒有改變。
如果你對這點(diǎn)有疑問,不妨自己寫寫例子試下,加深對React的理解。現(xiàn)在可以先記下結(jié)論:我們可以放心的使用多層高階組件,甚至重復(fù)地調(diào)用,而不必?fù)?dān)心影響輸出的DOM結(jié)構(gòu)。
借助函數(shù)的邏輯表現(xiàn)力,高階組件的用途幾乎是無窮無盡的:
適配器有的時(shí)候你需要替換一些已有組件,而新組件接收的參數(shù)和原組件并不完全一致。
你可以修改所有使用舊組件的代碼來保證傳入正確的參數(shù)——考慮改行吧如果你真這么想
也可以把新組件做一層封裝:
class ListAdapter extends Component { mapProps(props) { return {/* new props */} } render() { return} }
如果有十個(gè)組件需要適配呢?如果你不想照著上面寫十遍,或許高階組件可以給你答案
function mapProps(mapFn) { return function(Comp) { return class extends Component { render() { return} } } } const ListAdapter = mapProps(mapPropsForNewList)(NewList);
借助高階組件,關(guān)注點(diǎn)被分離得更加干凈:只需要關(guān)注真正重要的部分——屬性的mapping。
這個(gè)例子有些價(jià)值,卻仍然不夠打動人,如果你也這么想,請往下看:
處理副作用純組件易寫易測,越多越好,這是常識。然而在實(shí)際項(xiàng)目中,往往有許多的狀態(tài)和副作用需要處理,最常見的情況就是異步了。
假設(shè)我們需要異步加載一個(gè)用戶列表,通常的代碼可能是這樣的:
class UserList extends Component { constructor(props) { super(); this.state = { list: [] } } componentDidMount() { loadUsers() .then(data=> this.setState({list: data.userList}) ) } render() { return () } /* other bussiness logics */ }
實(shí)際情況中,以上代碼往往還會和其它一些業(yè)務(wù)函數(shù)混雜在一起——我們創(chuàng)建了一個(gè)業(yè)務(wù)與副作用混雜的、有狀態(tài)的組件。
如果再來一個(gè)書單列表呢?再寫一個(gè)BookList然后把loadUsers改成loadBooks ?
不僅代碼重復(fù),大量有狀態(tài)和副作用的組件,也使得應(yīng)用更加難以測試。
也許你會考慮使用Flux。它確實(shí)能讓你的代碼更清晰,但是在有些場景下使用Flux就像大炮打蚊子。比如一個(gè)異步的下拉選擇框,如果要考慮復(fù)用的話,傳統(tǒng)的Flux/Reflux幾乎無法優(yōu)雅的處理,Redux稍好一些,但仍然很難做優(yōu)雅。關(guān)于flux/redux的缺點(diǎn)不深入,有興趣的可以參考Cycle.js作者的文章
回到問題的本源:其實(shí)我們只想要一個(gè)能復(fù)用的異步下拉列表而已啊!
高階函數(shù)試試?
import React, { Component } from "react"; const DEFAULT_OPTIONS = { mapStateToProps: undefined, mapLoadingToProps: loading => ({ loading }), mapDataToProps: data => ({ data }), mapErrorToProps: error => ({ error }), }; export function connectPromise(options) { return (Comp) => { const finalOptions = { ...DEFAULT_OPTIONS, ...options, }; const { promiseLoader, mapLoadingToProps, mapStateToProps, mapDataToProps, mapErrorToProps, } = finalOptions; class AsyncComponent extends Component { constructor(props) { super(props); this.state = { loading: true, data: undefined, error: undefined, }; } componentDidMount() { promiseLoader(this.props) .then( data => this.setState({ data, loading: false }), error => this.setState({ error, loading: false }), ); } render() { const { data, error, loading } = this.state; const dataProps = data ? mapDataToProps(data) : undefined; const errorProps = error ? mapErrorToProps(error) : undefined; return (); } } return AsyncComponent; }; } const UserList = connectPromise({ promiseLoader: loadUsers, mapDataToProps: result=> ({list: result.userList}) })(List); //List can be a pure component const BookList = connectPromise({ promiseLoader: loadBooks, mapDataToProps: result=> ({list: result.bookList}) })(List);
不僅大大減少了重復(fù)代碼,還把散落各處的異步邏輯裝進(jìn)了可以多帶帶管理和測試的籠子,在業(yè)務(wù)場景中,只需要純組件 + 配置 就能實(shí)現(xiàn)相同的功能——而無論是純組件還是配置,都是對單元測試友好的,至少比異步組件友好多了。
使用curry & compose高階組件的另一個(gè)亮點(diǎn),就是對函數(shù)式編程的友好。你可能已經(jīng)注意到,目前我寫的所有高階函數(shù),都是形如:
config => { return Component=> { return HighOrderCompoent } }
表示為config=> Component=> Component。
寫成嵌套的函數(shù)是為了手動curry化,而參數(shù)的順序(為什么不是Component=> config=> Component),則是為了組合方便。關(guān)于curry與compose的使用,可以移步我的另一篇blog
舉個(gè)栗子,前面講了適配器和異步,我們可以很快就組合出兩者的結(jié)合體:使用NewList的異步用戶列表
UserList = compose( connectPromise({ promiseLoader: loadUsers, mapResultToProps: result=> ({list: result.userList}) }), mapProps(mapPropsForNewList) )(NewList);總結(jié)
在團(tuán)隊(duì)內(nèi)部分享里,我的總結(jié)是三個(gè)詞 Easy, Light-weight & Composable.
其實(shí)高階組件并不是什么新東西,本質(zhì)上就是Decorator模式在React的一種實(shí)現(xiàn),但在相當(dāng)一段時(shí)間內(nèi),這個(gè)優(yōu)秀的模式都被人忽略。在我看來,大部分使用mixin和class extends的地方,高階組件都是更好的方案——畢竟組合優(yōu)于繼承,而mixin——個(gè)人覺得沒資格參與討論。
使用高階組件還有兩個(gè)好處:
適用范圍廣,它不需要es6或者其它需要編譯的特性,有函數(shù)的地方,就有HOC。
Debug友好,它能夠被React組件樹顯示,所以可以很清楚地知道有多少層,每層做了什么。相比之下無論是mixin還是繼承,都顯得非常隱晦。
值得慶幸的是,社區(qū)也明顯注意到了高階組件的價(jià)值,無論是大家非常熟悉的react-redux 的connect函數(shù),還是redux-form,高階組件的應(yīng)用開始隨處可見。
下次當(dāng)你想寫mixin或class extends的時(shí)候,不妨也考慮下高階組件。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/78902.html
摘要:簡單來說高階組件就是一個(gè)函數(shù),它接受一個(gè)組件作為參數(shù)然后返回一個(gè)新組件。主要用于組件之間邏輯復(fù)用。使用由于數(shù)據(jù)請求是異步的,為了不讓用戶看到一片空白,當(dāng)數(shù)據(jù)請求還沒有返回時(shí),展示組件。組合函數(shù),提升代碼可閱讀性。 簡單來說高階組件(HOC)就是一個(gè)函數(shù),它接受一個(gè)組件作為參數(shù)然后返回一個(gè)新組件。HOC 主要用于組件之間邏輯復(fù)用。比如你寫了幾個(gè)組件,他們之間的邏輯幾乎相同,就可以用 HOC 對...
簡介:簡單實(shí)現(xiàn)react-redux基礎(chǔ)api react-redux api回顧 把store放在context里,所有子組件可以直接拿到store數(shù)據(jù) 使組件層級中的 connect() 方法都能夠獲得 Redux store 根組件應(yīng)該嵌套在 中 ReactDOM.render( , rootEl ) ReactDOM.render( ...
摘要:我們可以在組件的設(shè)計(jì)上,玩轉(zhuǎn)出很多花樣。但是,如何對一個(gè)功能復(fù)雜且臃腫的組件進(jìn)行分解,也許并不是一件簡單的事情。同時(shí),借助于新的算法引擎,兩個(gè)單元組件在渲染的效率上,樂觀地預(yù)計(jì)會有較大幅度的提升。 之前分享過幾篇關(guān)于React技術(shù)棧的文章: 做出Uber移動網(wǎng)頁版還不夠 極致性能打造才見真章 解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場景數(shù)據(jù)設(shè)計(jì) React Conf 2017 干貨總結(jié)1...
摘要:因?yàn)楣ぷ髦幸恢痹谑褂茫惨恢币詠硐肟偨Y(jié)一下自己關(guān)于的一些知識經(jīng)驗(yàn)。于是把一些想法慢慢整理書寫下來,做成一本開源免費(fèi)專業(yè)簡單的入門級別的小書,提供給社區(qū)。本書的后續(xù)可能會做成視頻版本,敬請期待。本作品采用署名禁止演繹國際許可協(xié)議進(jìn)行許可 React.js 小書 本文作者:胡子大哈本文原文:React.js 小書 轉(zhuǎn)載請注明出處,保留原文鏈接以及作者信息 在線閱讀:http://huzi...
摘要:栗子的方法就是一個(gè),他獲取,在中給添加需要的。本來準(zhǔn)備把詳細(xì)代碼當(dāng)個(gè)栗子貼出來的,結(jié)果突然想到公司保密協(xié)議,所以。。。栗子這樣子你就可以在父組件中這樣獲取的值了。 什么是HOC? HOC(全稱Higher-order component)是一種React的進(jìn)階使用方法,主要還是為了便于組件的復(fù)用。HOC就是一個(gè)方法,獲取一個(gè)組件,返回一個(gè)更高級的組件。 什么時(shí)候使用HOC? 在Reac...
閱讀 3814·2023-04-25 19:07
閱讀 3561·2021-11-22 12:02
閱讀 3143·2021-10-12 10:11
閱讀 3915·2021-09-03 10:49
閱讀 2894·2019-08-30 13:21
閱讀 3008·2019-08-30 11:14
閱讀 2093·2019-08-29 15:40
閱讀 2878·2019-08-28 18:29