基于react-hooks+Typescript二次封装Antd-Table

公司项目中有不少table页面,并且不少业务都很相似,CURD操做是不可避免的,这部分的操做逻辑很繁琐,页面维护形成极大的不方便,所以便想到使用hooks对table进行封装一波,抽离这些重复的逻辑。javascript

组件状态设计(props+state)

一般表格须要一份params搜索数据,params数据会根据不一样的业务逻辑而不一样,所以咱们做为prop传入组件内部, 除此而外咱们还需表格的列数据owncolumns,与antd-table组件原始支持的baseProps。在数据层面咱们须要在组件内部维护远程数据源datasource,根据不一样的业务传入不一样的查询方法queryAction,除此而外,咱们还须要一个loading状态,在请求数据的时候去显示loading动画,这个小东西对用户体验的影响很大。最后,咱们的组件的props与state就很清晰明了了java

const { owncolumns, queryAction, params, baseProps } = props
复制代码
const paginationInitial: paginationInitialType = {
    current: 1,
    pageSize: 10,
    total: 0,
}
复制代码

最后咱们合并这些statereact

const initialState: initialStateType = {
    loading: false,
    pagination: paginationInitial,
    dataSource: []
}
复制代码

逻辑设计

页面中须要维护的state都须要对应的操做去修改它,咱们之因此不用useState是由于,useState对不一样操做的细粒度不是很高,虽然能够合并state,可是对于不一样的操做,咱们须要更清晰的知道咱们的代码作了什么,好比说触发一个action,使用redux的思想,所以咱们天然而然想到了useState的代替方案-------useReducer。redux

const reducer = (state: initialStateType, action: actionType) => {
        const { payload } = action
        switch (action.type) {
            case 'TOGGLE_LOADING':  //更改loading状态
                return { ...state, loading: !state.loading }
            case 'SET_PAGINATION':  //设置分页数据
                return { ...state, pagination: payload.pagination }
            case 'SET_DATA_SOURCE': //设置远程数据源
                return { ...state, dataSource: payload.dataSource }
            default:
                return state
        }
    }
const [state, dispatch] = useReducer(reducer, initialState)
复制代码

此时咱们直接调用dispatch传入同的action就能够进入reducer进行处理,而且返回咱们想要的新的state后端

对接口的封装

页面中数据须要经过传入的queryAction进行查询,同时经过dispatch不一样的action去处理组件的状态缓存

async function fetchData() {
        dispatch({
            type: 'TOGGLE_LOADING'
        })
        // 分页字段名称转换
        const { current: indexfrom, pageSize: counts } = state.pagination
        let res = await queryAction({ indexfrom, counts, ...params }).catch(err => {
            dispatch({ type: 'TOGGLE_LOADING' })
            return {}
        })
        // 关闭loading
        dispatch({
            type: 'TOGGLE_LOADING'
        })
        if (res.result === 200) {
            const { totalcounts, list } = res
            // 这边根据不一样的后端接口去作处理
            dispatch({
                type: 'SET_PAGINATION',
                payload: {
                    pagination: { ...state.pagination, total: totalcounts }
                }
            })
            // 回填list数据
            dispatch({
                type: 'SET_DATA_SOURCE',
                payload: {
                    dataSource: list
                }
            })
        }
    }
复制代码

接着咱们确定会想到使用useEffect去为组件添加反作用,在组件mount,update时候去拉取数据,可是由于们的函数被定义在组件内部,组件每次更新都会从新生成该方法,对于useEffect来讲,他的依赖项每次组件update都会从新定义,依赖变化了,进而又会执行方法->组件更新->进而又会执行方法......,所以会形成死循环。有两种办法能够解决这个问题。bash

  1. 将函数提取到组件外部,同时将dispatch函数做为参数传入
  2. 使用useCallback函数优化,将方法进行缓存,只有当callback的依赖发生变化了才会再次执行fetchData方法。

第一种在这里很少说了,这里咱们采用第二种方式进行优化。antd

const fetchDataWarp = useCallback(
    fetchData,
    [params, state.pagination.current, owncolumns, queryAction],
)
 useEffect(() => {
    fetchDataWarp()
}, [fetchDataWarp])
复制代码

组件事件处理

目前咱们仅封装了分页的功能,所以只须要维护页面改变事件就能够async

// 改变页码
function handleTableChange(payload: any) {
if (payload) {
    const { current } = payload
    dispatch({
        type: 'SET_PAGINATION',
            payload: {
            pagination: {
                ...state.pagination,
                current
            }
        }
    })
}
}
复制代码

render

<Table
    columns={owncolumns(fetchData)}
    pagination={state.pagination}
    dataSource={state.dataSource}
    loading={state.loading}
    onChange={handleTableChange}
    {...baseProps}
/>
复制代码

TS类型

import { Columns } from '../../types/types'
import { TableProps } from 'antd/lib/table/interface'
interface queryActionType {
    (arg: any): Promise<any>
}
interface ColumnFunc {
    (updateMethod: queryActionType): Array<Columns>
}
export interface ArgTableProps {
    baseProps?: TableProps<any>
    owncolumns: ColumnFunc
    queryAction: queryActionType
    params: any
    listName?: string
}
export interface paginationInitialType {
    current: number
    pageSize: number
    total: number
}
export interface initialStateType {
    loading: boolean
    pagination: paginationInitialType
    dataSource: Array<any>
}
export interface actionType {
    type: string
    payload?: any
}
复制代码

组件完整代码

import React, { useEffect, useReducer, useCallback } from 'react'
import { Table } from 'antd';

import { ArgTableProps, paginationInitialType, initialStateType, actionType } from './type'

const useAsyncTable: React.FC<ArgTableProps> = props => {
    const { owncolumns, queryAction, params, baseProps } = props
    // 分页数据
    const paginationInitial: paginationInitialType = {
        current: 1,
        pageSize: 10,
        total: 0,
    }
    // table组件全量数据
    const initialState: initialStateType = {
        loading: false,
        pagination: paginationInitial,
        dataSource: []
    }
    const reducer = (state: initialStateType, action: actionType) => {
        const { payload } = action
        switch (action.type) {
            case 'TOGGLE_LOADING':
                return { ...state, loading: !state.loading }
            case 'SET_PAGINATION':
                return { ...state, pagination: payload.pagination }
            case 'SET_DATA_SOURCE':
                return { ...state, dataSource: payload.dataSource }
            default:
                return state
        }
    }
    const [state, dispatch] = useReducer(reducer, initialState)

    // 改变页码
    function handleTableChange(payload: any) {
        if (payload) {
            const { current } = payload
            dispatch({
                type: 'SET_PAGINATION',
                payload: {
                    pagination: {
                        ...state.pagination,
                        current
                    }
                }
            })
        }
    }
    // useCallback包装请求,缓存依赖,优化组件性能
    const fetchDataWarp = useCallback(
        fetchData,
        [params, state.pagination.current, owncolumns, queryAction],
    )
    async function fetchData() {
        dispatch({
            type: 'TOGGLE_LOADING'
        })
        // 分页字段名称转换
        const { current: indexfrom, pageSize: counts } = state.pagination
        let res = await queryAction({ indexfrom, counts, ...params }).catch(err => {
            dispatch({ type: 'TOGGLE_LOADING' })
            return {}
        })
        // 关闭loading
        dispatch({
            type: 'TOGGLE_LOADING'
        })
        if (res.result === 200) {
            const { totalcounts, list } = res
            dispatch({
                type: 'SET_PAGINATION',
                payload: {
                    pagination: { ...state.pagination, total: totalcounts }
                }
            })
            // 回填list数据
            dispatch({
                type: 'SET_DATA_SOURCE',
                payload: {
                    dataSource: list
                }
            })
        }
    }
    useEffect(() => {
        fetchDataWarp()
    }, [fetchDataWarp])
    return (
        <Table columns={owncolumns(fetchData)} pagination={state.pagination} dataSource={state.dataSource} loading={state.loading} onChange={handleTableChange} {...baseProps} /> ) } export default useAsyncTable 复制代码

README.md

Prop

属性 类型 默认值 备注
owncolumns (updatefunc:Function) : columns 必选参数 updatefunc用于刷新列表
queryAction (payload):Promise 必选参数 用于列表数据获取
baseProps TableProps from antd 任选 antd的基础props
params object {} 请求附加参数

使用例子

const getColumn: getColumnType = updateMethod => {
    return [
      {
        title: "项目名称",
        dataIndex: "project_name",
        key: "project_name",
      },
      {
        title: '操做',
        key: 'setting',
        width: 200,
        render: (text: any, record: any, index: number) => {
          return (
            <div> <Button type="primary" style={{ marginRight: '5px' }}>查看</Button> <Popconfirm title="此操做将永久删除该项目, 是否继续?" okText="肯定" cancelText="取消" onConfirm={() => { updateMethod() }} > <Button type="danger">删除</Button> </Popconfirm> </div>
          )
        }
      }
    ];
  }
  render(){
      return (
          <ArgTable owncolumns={updatefunc => getColumn(updatefunc)} queryAction={API.http_getProjectList} baseProps={{ rowKey: record => record.project_id }} params={searchData} /> ) } 复制代码
相关文章
相关标签/搜索