react實現一個分頁、搜索高階組件

1、前言

先感謝掘金這個平臺讓我能夠看到別人的思想,別人的智慧。說真的,在這個平臺上仍是看到了許多有養分的文章。目前的我是產不出像大佬那樣富有養分的文章,那就記錄一點我在工做中的事情,若有說錯的地方或是有不合理的地方,但願各位大佬指出。javascript

項目的背景是作一個總管理後臺,個人那個項目直接用的就是螞蟻金服的ant-design-pro,不得不誇讚這個真的是太好用了。而後作這種管理後臺老是會遇到不少表格,列表之類的展現,通常的表格和列表都會有兩個最基本的訴求,那就是分頁和搜索。而後個人代碼中就充斥了不少這樣的邏輯。 在 componentDidMount 的時候發送請求加載數據,在頁碼改變的時候加載數據,在客戶搜索的時候加載數據......html

總之就是我寫了不少功能重複的代碼,不行,我是一個善於思(做)考(死)的人,我要想一個辦法去解決這個問題!而後我感受react的高階組件能夠應用於此場景。java

2、思考這個高階組件要解決什麼問題

本文就不在此說高階組件究竟是什麼,本文主要探討的目標在於如何利用高階組件去解決問題,若是有對高階組件不甚瞭解的同窗能夠先看看 react官方文檔react

接下來的探討就基本我本次開發的項目中,先講一下本次項目開發採用的技術棧。本次是開發一個後臺管理系統,項目使用了 antddva。 其實其餘的都不重要了,最重要的是這個 dva ,使用過的同窗應該都知道,它提供了一個 model 的概念。也就是說咱們所獲取的數據是要用這個 model 去管理的。我這個高階組件要實現的最基本的功能有一下兩點:git

  • 翻頁獲取數據github

  • 可以根據表單搜索數據redux

3、開始編寫

一、搭好架子

廢話就很少說了,開始編寫這個高階組件,先把這個組件寫出來再說!新建一個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 本來恰好是一個路由組件,當你在定義路由的頁面去引用這個組件的時候,它已經被高階組件包裹了,就是說對本來的這個 WarpComponentprops 傳遞的值如今都傳遞到了咱們所編寫的高階組件上去了,因此,在這裏,咱們將本來屬於WarpComponentprops 「還」 給它。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.dispathdva 提供給個人方法,我只須要根據 dva 定義的去使用去就能夠了。 若是想直接使用 redux 的話按照 redux 那一套就能夠了,使用 redux 的話這裏就是 發起一個 action, 那麼很簡單,傳進來的那個 type 只要改爲 reduxaction 就能夠了。

而後還有一個 resetData 方法,很明顯的若是要 reset 的話,須要重置頁碼還有搜索參數:

resetData = ()=> {
    this.setState({
        page: 1,
        formValues: {}
    }, this.getData)
}
複製代碼

而後是獲取數據,通常都是在 componentDidMount 生命週期裏去獲取。因此在加上代碼:

componentDidMount() {
   this.getData()
}
複製代碼

四、如何使用

組件的代碼咱們是已經編寫完了。組件很簡單,使用起來也很簡單。只須要在頁碼改變的時候,或者是搜索參數改變的時候去調用這個高階組件所提供的 handlePageChange 方法和 searchData 方法就能夠了。這裏我提供一個使用範例,範例中有完整的高階組件的代碼,也有完整的使用的代碼。 在這個項目中使用了 antd dva ,很真實的使用環境,但願能夠提供幫助。在項目數據mock服務中我只處理了頁碼,沒有去管搜索參數,有疑問能夠打開 network看看搜索參數是否正常傳遞。

若是有疑問能夠在掘金下回復我,也能夠在我貼出的git項目的 issue中提出問題,我會盡力解答。

4、總結

文章寫完啦,深深的感受本身的陳述能力不足。言歸正傳,這就是一次高階組件的使用,其實不少設計都是用到了高階組件,就好比說 reduxconnect , 用過 antd 的朋友應該也知道在表單驗證的時候有 Form.creategetFieldDecorator 。這些都是很經典的使用,平時我也喜歡用高階組件去作一些權限處理啊什麼的。 雖說如今 react 已經推出了 hooks ,可能 class 類型的組件被淘汰掉只是時間的問題。試着去了解了一下 hooks 以爲我get不到其中的思想。等後續我再試着瞭解 hooks 看看能不能帶來一個 hooks 的實現版本。

水平有限,可能說的有一些錯的地方我卻不自知, 但願你們指正。

最後:

使用範例地址:github.com/Chechengyi/…

個人 github地址:github.com/Chechengyi

碼字不易,歡迎朋友們來一波 star 或者 贊。

相關文章
相關標籤/搜索