當前位置:
首頁 > 科技 > 面向初學者的高階組件教程

面向初學者的高階組件教程

前言

本期早讀文章由@冉余翻譯分享。

正文從這開始~

一篇面向初學者的 HOC 介紹。高階組件聽起來挺唬人的,只看名字恐怕不是那麼容易理解,而且高階組件不是組件,而是接受組件作為參數,並且返回組件的函數。早期利用 ES5 的 mixin 語法來做的事,基本都可以使用高階組件代替,而且能做的還有更多。

寫這篇文章的起因是其他關於高階組件(Higher-Order Components)的文章,包含官方文檔,都令初學者感到相當困惑。我知道有高階組件這樣一個東西,但不知道它到底有什麼用。所以,想通過一篇文章來對高階組件有一個更好的理解。

在此之前,我們需要先來講一下 JavaScript 中的函數。

ES6 箭頭函數簡介

接下來將提供一些箭頭函數的簡單示例,如果之前沒有使用過,可以認為它們與普通函數基本一致。下面的代碼會展示箭頭函數與普通函數的區別。

function(){

return42

}

// same as:

()=>42

// same as:

()=>{

return42

}

functionperson(name){

return{name:name}

}

// same as:

(name)=>{

return{name:name}

}

閱讀 MDN 的箭頭函數文檔 了解更多信息。

作為值得函數與部分調用

就像是數字、字元串、布爾值 一樣,函數也是值,意味著可以像傳遞其他數據一樣傳遞函數,可以將函數作為參數傳遞給另外一個函數。

constexecute=(someFunction)=>someFunction()

execute(()=>alert( Executed ))

也可以在在函數中返回一個函數:

constgetOne=()=>()=>1

getOne()()

之所以在 getOne 後面有兩個 () ,是因為第一個返回的返回值是一個函數。如下:

constgetOne=()=>()=>1

getOne

//=> () => () => 1

getOne()

//=> () => 1

getOne()()

//=> 1

從函數返回函數可以幫助我們追蹤初始輸入函數。例如,下面的函數接受一個數字作為參數,並返回一個將該參數乘以新參數的函數:

constmultiply=(x)=>(y)=>x*y

multiply(5)(20)

這個示例跟上述 getOne 一樣,在下面這個例子,讓 x = 5,y = 20。

constmultiply=(x)=>(y)=>x*y

multiply

//=> (x) => (y) => x * y

multiply(5)

//=> (y) => 5 * y

multiply(5)(20)

//=> 5 * 20

在只傳入一個參數調用 multiply 函數時,即部分調用該函數。比如,multiply(5) 講得到一個將其輸入值乘以 5 的函數,multiply(7) 將得到一個將其輸入值乘以 7 的函數。依此類推。通過部分調用可以創建一個預定義功能的新函數:

constmultiply=(x)=>(y)=>x*y

constmultiplyByFive=multiply(5)

constmultiplyBy100=multiply(100)

multiplyByFive(20)

//=> 100

multiply(5)(20)

//=> 100

multiplyBy100(5)

//=> 500

multiply(100)(5)

//=> 500

一開始看起來似乎沒什麼用,但是,通過部分調用這種方式可以編寫可讀性更高,更易於理解的代碼。舉個例子,可以用一種更清晰的方式來代替 style-components 的函數插入語法。

// before

constButton=styled.button`

background-color:${({theme}) => theme.bgColor}

color:${({theme}) => theme.textColor}

`

Submit

// after

constfromTheme=(prop)=>({theme})=>theme[prop]

constButton=styled.button`

background-color:${fromTheme("bgColor")}

color:${fromTheme("textColor")}

`

Submit

我們創建一個接受一個字元串作為參數的函數 fromTheme("textColor"):它返回一個接受具有 theme 屬性的對象的函數:({ theme }) => theme[prop],然後再通過初始傳入的字元串 "textColor" 進行查找。我們可以做得更多,寫類似的 backgroundColor 和 textColor 這種部分調用 fromTheme 的函數:

constfromTheme=(prop)=>({theme})=>theme[prop]

constbackgroundColor=fromTheme("bgColor")

consttextColor=fromTheme("textColor")

constButton=styled.button`

background-color:${backgroundColor}

color:${textColor}

`

Submit

高階函數

高階函數的定義是,接受函數作為參數的函數。如果曾經使用過類似 map 這樣的函數,可能已經很熟悉高階函數。如果不熟悉 map,它是一個數組遍歷的方法,接受一個函數作為參數應用到數組中的每個元素。例如,可以像這樣對一個數組作平方:

constsquare=(x)=>x*x

[1,2,3].map(square)

//=> [ 1, 4, 9 ]

可以實現一個我們自己的 map 版本來說明這個概念:

constmap=(fn,array)=>{

constmappedArray=[]

for(leti=;i

mappedArray.push(

// apply fn with the current element of the array

fn(array[i])

)

}

returnmappedArray

}

然後再使用我們的 map 版本來對一個數組作平方:

constsquare=(x)=>x*x

console.log(map(square,[1,2,3,4,5]))

//=> [ 1, 4, 9, 16, 25 ]

這樣也可以像上述例子一樣調用。或者更函數式的做法,再來點柯里化:

或者是返回一個 的 React 元素數組:

constHeroList=({heroes})=>(

{map((hero)=>(

{hero}

),heroes)}

)

"Wonder Woman",

"Black Widow",

"Spider Man",

"Storm",

"Deadpool"

]/>

/*=> (

Wonder Woman

Black Widow

Spider Man

Storm

Deadpool

)*/

高階組件

我們知道,高階函數是接受函數作為參數的函數。在 React 中,任何返回 JSX 的函數都被稱為無狀態函數組件,簡稱為函數組件。基本的函數組件如下所示:

constTitle=(props)=>{props.children}

Higher-OrderComponents(HOCs)forReact Newbies

//=> Higher-Order Components(HOCs) for React Newbies

高階組件則是接受組件作為參數並返回組件的函數。如何使用傳入組件完全取決於你,甚至可以完全忽視它:

// Technically an HOC

constignore=(anything)=>(props)=>:)

constIgnoreHeroList=ignore(HeroList)

//=> :)

可以編寫一個將輸入轉換成大寫的 HOC:

constyell=(PassedComponent)=>

({children,...props})=>

{children.toUpperCase()}!

constTitle=(props)=>{props.children}

constAngryTitle=yell(Title)

Whatever

//=> WHATEVER!

你也可以返回一個有狀態組件,因為 JavaScript 中的類不過是函數的語法糖。這樣就可以使用到 React 生命周期的方法,比如 componentDidMount。這是 HOCs 真正有用的地方。我們現在可以做一些稍微有趣點的事,比如將 HTTP 請求的結果傳遞給函數組件。

constwithGists=(PassedComponent)=>

classWithGistsextendsReact.Component{

state={

gists:[]

}

componentDidMount(){

fetch("https://api.github.com/gists/public")

.then((r)=>r.json())

.then((gists)=>this.setState({

gists:gists

}))

}

render(){

return(

{...this.props}

gists={this.state.gists}

/>

)

}

}

constGists=({gists})=>(

{JSON.stringify(gists,null,2)}

)

constGistsList=withGists(Gists)

//=> Before api request finishes:

//

//

//=> After api request finishes:

//

// {/* … */},

// {/* … */},

// {/* … */}

// ]} />

withGists 會傳遞 gist api 調用的結果,並且你可以在任何組件上使用。點擊這裡 可以看到一個更加完整的例子。

結論:高階組件是

react-redux 也是使用 HOC, connect 將應用 store 的值傳遞到「已連接」 的組件。它還會執行一些錯誤檢查和組件生命周期優化,如果手動完成將導致編寫大量重複代碼。

如果你發現自己在不同地方編寫了大量的代碼,那麼也可以將代碼重構成可重用的 HOC。

HOCs 非常具有表現力,可以使用它們創造很多很酷的東西。

儘可能地保持你的 HOC 簡單,不要編寫需要閱讀長篇大論才能理解的代碼。

附加練習

下面有一些練習,來鞏固對 HOC 的理解:

寫一個反轉其輸入的 HOC

編寫一個HOC,將 API 中的數據提供給組件

寫一個HOC來實現 shouldComponentUpdate,以避免更新。

編寫一個 HOC,使用 React.Children.toArray 對傳入組件子元素進行排序。

關於本文

譯者:@冉余

作者:@Brandon Newton

原文:https://btnwtn.com/articles/higher-order-components-for-beginners

點擊展開全文

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端早讀課 的精彩文章:

構建高性能展開&收縮動畫
BATJ 前端面試的 5 大關鍵點,你 Get 到了嗎?
坦然面對:應對前端疲勞
JSON schema與表單驗證

TAG:前端早讀課 |

您可能感興趣

學習es7的Decorator(順帶寫個react高階組件)