先感謝掘金這個平臺讓我能夠看到別人的思想,別人的智慧。說真的,在這個平臺上仍是看到了許多有養分的文章。目前的我是產不出像大佬那樣富有養分的文章,那就記錄一點我在工做中的事情,若有說錯的地方或是有不合理的地方,但願各位大佬指出。javascript
項目的背景是作一個總管理後臺,個人那個項目直接用的就是螞蟻金服的ant-design-pro,不得不誇讚這個真的是太好用了。而後作這種管理後臺老是會遇到不少表格,列表之類的展現,通常的表格和列表都會有兩個最基本的訴求,那就是分頁和搜索。而後個人代碼中就充斥了不少這樣的邏輯。 在 componentDidMount
的時候發送請求加載數據,在頁碼改變的時候加載數據,在客戶搜索的時候加載數據......html
總之就是我寫了不少功能重複的代碼,不行,我是一個善於思(做)考(死)的人,我要想一個辦法去解決這個問題!而後我感受react的高階組件能夠應用於此場景。java
本文就不在此說高階組件究竟是什麼,本文主要探討的目標在於如何利用高階組件去解決問題,若是有對高階組件不甚瞭解的同窗能夠先看看 react官方文檔 。react
接下來的探討就基本我本次開發的項目中,先講一下本次項目開發採用的技術棧。本次是開發一個後臺管理系統,項目使用了 antd
、dva
。 其實其餘的都不重要了,最重要的是這個 dva
,使用過的同窗應該都知道,它提供了一個 model
的概念。也就是說咱們所獲取的數據是要用這個 model
去管理的。我這個高階組件要實現的最基本的功能有一下兩點:git
翻頁獲取數據github
可以根據表單搜索數據redux
廢話就很少說了,開始編寫這個高階組件,先把這個組件寫出來再說!新建一個table.js
文件,這個文件有如下內容:antd
import React from 'react'
export default (WarpComponent)=> {
return class extends React.Component {
state = {
formValues: {} // 表單搜素字段
page: 1, // 分頁頁碼
num: 10
}
render(){
return (
<WarpComponent {...this.props} /> ) } } } 複製代碼
如今寫出來的這個高階組件已經能夠直接使用了,使用的時候只須要:函數
import TableHoc from 'xxx'
@TabelHoc
export default class XXX {...}
// 至關於 TableHoc(XXX)
複製代碼
能夠看見其實高階組件就是一個函數,接收了組件而後返回了組件。能夠看到在渲染被包裹的那個組件的時候把 props
所有解構給了被包裹的這個組件,這並非畫蛇添足。想象一下若是這個WarpComponent
本來恰好是一個路由組件,當你在定義路由的頁面去引用這個組件的時候,它已經被高階組件包裹了,就是說對本來的這個 WarpComponent
的 props
傳遞的值如今都傳遞到了咱們所編寫的高階組件上去了,因此,在這裏,咱們將本來屬於WarpComponent
的 props
「還」 給它。this
在上面的編寫中我把分頁的頁碼和條數都設定死了,其實這是很不友好的。咱們應該讓它變成一個能夠配置的項。修改以上的代碼爲:
import React from 'react'
export default ({type, page, num}) => WrapComponent => {
return class extends React.Component {
state = {
formValues: {},
page: page || 1,
num: num || 10
}
render(){
return (
<WarpComponent {...this.props} /> ) } } } 複製代碼
在使用的時候只須要:
import TableHoc from 'xxx'
@TableHoc({
page: 2, // 這裏傳了多少那高階組件裏初始化的值就是多少
num: 20,
type: 'xxx'
})
export default class XXX {...}
複製代碼
這裏說一下這個 type
是什麼,其實這個 type
需不須要取決於你本身的項目。好比我這個項目,我用了 dva
那數據我都是用集中管理在 models
的。用過 dva
的朋友都知道,它有一個 命名空間
的概念,就是在 dispatch
的時候有一個 type
須要咱們去傳嘛。若是你沒有使用 dva
或者 redux
。 那就直接在這個高階組件裏去管理、獲取數據而後做爲 props
傳給被包裹的組件就好了,那相應的你也不須要這個 type
參數了。
還有一個問題,就是這個高階組件它須要去執行 dispatch
操做嘛,那就先須要 connect
,注意,這裏你是使用的 dva
仍是 redux
其實根本沒區別, 本質上這個 connect
也是一個高階組件。如今我把 connect
寫上去:
import TableHoc from 'xxx'
import { connect } from 'dva'
@connect()
@TableHoc({
page: 2, // 這裏傳了多少那高階組件裏初始化的值就是多少
num: 20,
type: 'xxx'
})
export default class XXX {...}
複製代碼
須要注意的是這個 connect
必定要寫到 TableHoc
前面。
如今要說的就是這個組件核心的功能,翻頁和搜索,如今 TableHoc
組件裏寫上兩個方法:
searchData = formValues => {
this.setState({
page: 1,
formValues
}, this.getData)
}
handlePageChange = (page, num) => {
this.setState({
page,
num
}, this.getData)
}
複製代碼
先不去管 this.getData
這個方法,這個兩個方法明顯就是要給被包裹的組件使用的,因此要傳給被包裹的組件。還有頁碼,被包裹的組件在翻頁的時候確定也是須要展現頁碼的,因此都傳下去。
改寫 render
方法:
render(){
return (
<WrapComponent ref={com => this.warpCom = com} {...this.props} {...this.state} searchData = {this.searchData} handlePageChange = {this.handlePageChange} resetData = {this.resetData} /> ) } 複製代碼
能夠注意我給被包裹的組件加上了一個 ref
屬性,這樣我就能夠拿到組件的實例了。爲何要加呢,主要是我考慮到一種狀況,就是除了翻頁、還有表單搜索這樣的一些搜索字段,可能還有一個額外的參數須要用做搜索,因此我把這個決定的權利給到被包裹的組件。接下來完善 this.getData
方法:
getData = ()=> {
let elseSearchPrams = {}
if ( this.warpCom && this.warpCom.getSearchParams ) {
elseSearchPrams = this.warpCom.getSearchParams()
}
const { page, num, formValues } = this.state
this.props.dispatch({
type,
payload: {
page,
num,
...formValues,
...elseSearchPrams
}
})
}
複製代碼
能夠看到我試着去獲取了一下 warpCom
有沒有額外須要傳入搜索的參數,在 warpCom
中只須要去定義一個名爲 getSearchParams
的方法,而後返回一個包含搜索信息的對象就能夠了。這裏的 this.props.dispath
是 dva
提供給個人方法,我只須要根據 dva
定義的去使用去就能夠了。 若是想直接使用 redux
的話按照 redux
那一套就能夠了,使用 redux
的話這裏就是 發起一個 action
, 那麼很簡單,傳進來的那個 type
只要改爲 redux
的 action
就能夠了。
而後還有一個 resetData
方法,很明顯的若是要 reset
的話,須要重置頁碼還有搜索參數:
resetData = ()=> {
this.setState({
page: 1,
formValues: {}
}, this.getData)
}
複製代碼
而後是獲取數據,通常都是在 componentDidMount
生命週期裏去獲取。因此在加上代碼:
componentDidMount() {
this.getData()
}
複製代碼
組件的代碼咱們是已經編寫完了。組件很簡單,使用起來也很簡單。只須要在頁碼改變的時候,或者是搜索參數改變的時候去調用這個高階組件所提供的 handlePageChange
方法和 searchData
方法就能夠了。這裏我提供一個使用範例,範例中有完整的高階組件的代碼,也有完整的使用的代碼。 在這個項目中使用了 antd
dva
,很真實的使用環境,但願能夠提供幫助。在項目數據mock服務中我只處理了頁碼,沒有去管搜索參數,有疑問能夠打開 network看看搜索參數是否正常傳遞。
若是有疑問能夠在掘金下回復我,也能夠在我貼出的git項目的 issue中提出問題,我會盡力解答。
文章寫完啦,深深的感受本身的陳述能力不足。言歸正傳,這就是一次高階組件的使用,其實不少設計都是用到了高階組件,就好比說 redux
的 connect
, 用過 antd
的朋友應該也知道在表單驗證的時候有 Form.create
和 getFieldDecorator
。這些都是很經典的使用,平時我也喜歡用高階組件去作一些權限處理啊什麼的。 雖說如今 react
已經推出了 hooks
,可能 class
類型的組件被淘汰掉只是時間的問題。試着去了解了一下 hooks
以爲我get不到其中的思想。等後續我再試着瞭解 hooks
看看能不能帶來一個 hooks
的實現版本。
水平有限,可能說的有一些錯的地方我卻不自知, 但願你們指正。
最後:
使用範例地址:github.com/Chechengyi/…
個人 github地址:github.com/Chechengyi
碼字不易,歡迎朋友們來一波 star 或者 贊。