First look at the final code:

import React, { useState, useMemo, useEffect } from 'react'Interface PageAbout {current: number pageSize: number} // With antD table, production table required function method and stateexport function useTableData<
  TableParams extends SearchData & PageAbout = any,
  SearchData extends object = any,
  Datasource = any
>({
  tableParamsInit = { pageSize: 10, current: 1 } as any,
  searchDataInit = {},
  pullData = () => {
    // console.log('pullData')
  },
  getTotal = () => {
    return1 }, }: { tableParamsInit? : TableParams searchDataInit? : SearchData | {} / / call the method of the interface, accept itsetUseTableData pullData: (params: TableParams,setDatasource: React. Dispatch < React. SetStateAction < Datasource [] | [] > >) = > void getTotal total / / get the data from the results? : (res: any) => number }) { const [datasource,setDatasource] = useState<Datasource[] | []>([])
  const [tableParams, setTableParams] = useState<TableParams>(tableParamsInit)
  const [searchData, setSearchData] = useState<SearchData | {}>(searchDataInit)
  const [total, setUseEffect (() => {const res = pullData(tableParams,setDatasource)
    setTotal(getTotal(res))}, [tableParams]) // State will be searched when searchingsetConst handleSearch = () => {const handleSearch = () => {setTableParams((s) => ({ ... s, current: 1, ... SearchData}))} // reset the search dependency and table interface dependency to init const handleReset = () => {setTableParams(tableParamsInit)
    setSearchData(searchDataInit)} // Page number change const handlePageChange = (v: number) => {setTableParams((s) => ({ ... s, current: Const handleAfterDel = (count) const handleAfterDel = (count) const handleAfterDel = (count? : number) => {// get new data directly on the first pageif(datasource? .length > (count ?? 1) || tableParams.current === 1) { const res = pullData(tableParams,setDatasource)
      setTotal(getTotal(res))
    } elseConst deletedPage = math.floor (Number((((count?? 1) - (datasource? .length ?? 0)) / tableParams.pageSize ).toFixed(0) ) )setTableParams((s) => ({ ... s, current: s.current - deletedPage > 1 ? s.current - deletedPage : 1, })) } }return{// Total number of datasetTotal, //setDatasource, // Table interface depends on tableParams,setTableParams, // search depends on state searchData,setSearchData, // Search handleSearch, // reset handleReset, // page change handlePageChange, // delete data after execution, parameter is the number of deleted handleAfterDel,}}Copy the code

Packaging ideas

Hooks provide logic reuse capabilities that were not easily implemented in the react style. The author has been using hooks in production practice for nearly two months, and has written a lot of tables in background projects, so the TABLE component based on ANTD has packaged the corresponding reusable logic.

  • Parameters of the part

In general, all the different parts of the table that will appear need to be passed in as parameters to the hooks. 1. Get the initial value of params parameter required for table data: tableParamsInit (default page-turning operation required) 2. Initial values of parameters on which the query, filtering, and search functions depend: searchDataInit 3. 4. Method to get the total number of data (if the background interface format is almost the same, this can also be written dead, do not pass)

{
  tableParamsInit = { pageSize: 10, current: 1 } as any,
  searchDataInit = {},
  pullData = () => {
    // console.log('pullData')
  },
  getTotal = () => {
    return1 }, }: { tableParamsInit? : TableParams searchDataInit? : SearchData | {} / / call the method of the interface, accept itsetUseTableData pullData: (params: TableParams,setDatasource: React. Dispatch < React. SetStateAction < Datasource [] | [] > >) = > void getTotal total / / get the data from the results? : (res: any) => number }Copy the code
  • State of the state

    TableParams is the final parameter used to send the interface request. 3. SearchData is the state of the query state

  const [datasource, setDatasource] = useState<Datasource[] | []>([])
  const [tableParams, setTableParams] = useState<TableParams>(tableParamsInit)
  const [searchData, setSearchData] = useState<SearchData | {}>(searchDataInit)
Copy the code
  • Listening to the effect
    • Monitoring tableParams is the change of interface parameters, interface dependent parameters change request interface to obtain new data, this call interface timing is the actual production of the author summarizes the logic is more clear and easy to understand a kind of error.
    • It’s worth noting that the parameters used for query searches are only added to tableParams when the user clicks the query or search button, triggering the interface to retrieve the data. If the reader is in production and the product needs to search for changes in content and immediately retrieve the data, the parameters that the search depends on should be stored directly in tableParams rather than in searchData.
 useEffect(() => {
   const res = pullData(tableParams, setDatasource)
   setTotal(getTotal(res))
 }, [tableParams])
Copy the code
  • Methods handler
    1. HandleSearch is usually added directly to the query button by the author. It assigns the searchData value to tableParams, changes the tableParams, and triggers pullData to fetch new data. Note: Current is also reassigned to 1 by the authors because the queried or filtered data will generally be less than the total data. If the page count is not reset to 1, the query results will not meet the expectations.
// When searching, state is searchedsetConst handleSearch = () => {const handleSearch = () => {setTableParams((s) => ({ ... s, current: 1, ... searchData })) }Copy the code
    1. The handleReset method is usually placed on the reset button by the author. It triggers pullData by reassigning the values of tableParams and searchData to init values, and overwrites the original data.
// reset the search dependency and table interface dependency to init const handleReset = () => {setTableParams(tableParamsInit)
    setSearchData(searchDataInit)
  }
Copy the code
    1. HandlePageChange page turn, directly to antD Table component pagination onChange ok
// Page number change const handlePageChange = (v: number) => {setTableParams((s) => ({ ... s, current: v })) }Copy the code
    1. HandleAfterDel When a table has the ability to delete data, you have to consider some processing logic after deleting data.
    • The first page gets the new data directly with the current parameters
    • If the amount of current data is greater than the amount of deleted data, use the current parameters to obtain new data
    • Calculate the number of pages that were deleted and the last page that should have data, 0 or negative should be the first page
Const handleAfterDel = (count) const handleAfterDel = (count? : number) => {// get new data directly on the first pageif(datasource? .length > (count ?? 1) || tableParams.current === 1) { const res = pullData(tableParams,setDatasource)
      setTotal(getTotal(res))
    } elseConst deletedPage = math.floor (Number((((count?? 1) - (datasource? .length ?? 0)) / tableParams.pageSize ).toFixed(0) ) )setTableParams((s) => ({ ... s, current: s.current - deletedPage > 1 ? s.current - deletedPage : 1, })) } }Copy the code
  • use
. const { total,setTotal, //setDatasource, // Table interface depends on tableParams,setTableParams, // search depends on state searchData,setSearchData, // Search handleSearch, // reset handleReset, // page change handlePageChange, // delete data after execution, parameter is the number of deleted handleAfterDel, } = useTableData<any, any, any>({ tableParamsInit: { pageSize: 10, current: 1 }, searchDataInit: { discount_type: 0 },getTotal() {
      return 100
    },
    pullData({ pageSize: limit, current: page, ... rest },setData) {
      dispatch({
        type: `${namespace}/${ActionTypes.publicCodeGet}`,
        payload: {
          params: {
            limit, page, ... rest, }, onSuccess(res) {setData(res? .data) }, }, })setData(MOCK.publicCodeTableData)
    },
  })

return <>  
      <Form className={`${styles.search} mt20`} layout="inline">
          <Form.Item label="Generic code name">
            <Input
              placeholder="Please enter common code name"
              value={searchData.keywords}
              onPressEnter={handleSearch}
              onChange={({ target: { value } }) => {
                setSearchData((s) => ({ ... s, keywords: value })) }} ></Input> </Form.Item> <Form.Item label="Preferential mode">
            <div style={{ width: 180 }}>
              <Select
                className={`w100`}
                placeholder="Please select a discount option"
                options={[{ label: 'all', value: 0 }, ... types.discount.list]} value={searchData.discount_type} onChange={(v) => {setSearchData((s) => ({ ... s, discount_type: v })) }} ></Select> </div> </Form.Item> <Form.Item> <Button onClick={handleSearch}type="primary"</Button> </ form. Item> < form. Item> <Buttontype="ghost"OnClick ={handleReset}> reset </Button> </ form. Item> </Form> <Table loading={tableLoading} dataSource={dataSource} rowKey="id"Pagination ={{total, pageSize: tableparams. pageSize, current: tableparams. current, showTotal: (total) => 'total${total}ShowSizeChanger:false,
              onChange: handlePageChange,
          }}
          columns={[
            { title: 'Generic code name', dataIndex: 'name' },
            {
              title: 'Scope of application',
              dataIndex: 'range',
              render(_, record) {
                return (
                  <div>
                    <div className={`p16`}>
                      {types.goods.data[record.range.goods_type]}
                    </div>
                    <div className={`p12g`}>{record.range.name}</div>
                  </div>
                )
              },
            },
            { title: 'Promo code', dataIndex: 'code' },
            { title: 'Preferential method', align: 'center', dataIndex: 'path' },
            { title: 'Available quantity', align: 'center', dataIndex: 'count' },
            {
              title: Term of validity,
              dataIndex: 'time',
              render(time) {
                return(< div > < div > : {time [0]} < / div > < div > check: {time [1]} < / div > < / div >)},}, {title:'state',
              align: 'center',
              dataIndex: 'is_enable',
              render(text) {
                return text === 1 ? 'enable' : 'disabled'
              },
            },
            {
              title: 'operation',
              dataIndex: 'option',
              align: 'center',
              key: 'option',
              width: 120,
              render: (text: any, record: any, index) => (
                <span className={styles.operate}>
                  <a
                    onClick={() => {
                      router.push({
                        pathname: '/marketing/publicCode/form',
                        query: { id: record.id ?? ' ' },
                      })
                    }}
                  >
                    编辑
                  </a>
                  <Divider type="vertical" />
                  <Popover
                    placement="bottom"
                    content={
                      <div>
                        <p
                          className="hoverActive cp pb4"
                          onClick={() => {
                            router.push({
                              pathname: '/marketing/publicCode/detail', query: {id: record.id},})}} > Usage </p> <p className="hoverActive cp pb4"onClick={() => { // handleDel(record.id); HandleDel (record.id, index)}} > Remove </p> </div>} trigger="hover"> < a onClick = {() = > {}} > more < / a > < / Popover > < / span >),},]} / > < / a >...Copy the code

Summary: Type checking and other areas need to be optimized. The author level is limited, still ask each reader big guy to be done right.