基于React Hook + TS + Ant 的table分页查询功能小组件

2,253 阅读4分钟

背景

在PC的开发项目中,一定会有一些列表查询的页面,无外乎,就是查询条件和table中回显的数据的不同,然后再加上一个分页功能,为了提高效率,于是准备将整个查询与更改页码的封装在一个组件当中,使用时只需将组件引入,传入所需要的参数就可以使用,数据请求部分用一个自定义的hook来解决,这部分是借鉴了别人的写法链接,觉得很好,就放到自己代码中,也进行了一些改动。hook的基本使用就不再赘述,如果还不了解的,可以去React 官网。那么剩下的,需要的准备工作就是:

  1. 封装一个自定义的useService的hook,来负责数据请求的部分
  2. 封装table组件

封装自定义 useService

该部分主要负责数据查询,然后将 { loading, response } 暴露出来。每次借用hook特性,依赖 params 的改变,从而重新请求数据。

import { useEffect, useRef, useState } from 'react';
import { isEqual } from 'lodash';
interface res {
  result: number;
  message: string;
  totalcounts: number;
  totalpage: number;
  [key : string ]: any;
}
const useService = (service: (param: object) => Promise<void>, params: any) => {
  const prevParams = useRef(null);
  const [loading, setloading] = useState(false);
  const [response, setResponse] = useState<res>();
  useEffect(() => {
    if(!isEqual(prevParams.current, params)) {
      prevParams.current = params;
      setloading(true);
      service(params)
        .then(( res: any )=>{
          setResponse(res);
          setloading(false);
        })
        .catch( (error: any) => {
          setloading(false);
          console.log('error', error);
        })
    }
  }, [service, params])
  
  return { loading, response }
}

export default useService;

封装 table 组件

这个部分将整个 table 和 pagination 组件封装起来,只接受请求地址和参数,切换页码的操作放在组件内部,父组件使用的时候,不必在考虑页码的操作,如果你要问为什么单独使用pagination,我只想说个人习惯而已,也可以table自带的,loading的加载组件也是使用的Ant自带的,这些如果你有兴趣,也可以自己写。 children 的话,是放搜索框或者下拉选择框,只是设置查询条件,没有的话可以不加。

useService 使用的时候,就像自定义hook的使用方法一样使用就可以。如果 params 改变,就会触发Ajax请求,获取最新的数据。 updateInPropsParams 这个函数作用主要是用于查询条件更改时,传入 ComponentTable 时的 queryparams 更改时触发,虽然依赖只写 queryparams ,编辑器会报警告,但切忌只写这个,因为只是需要 queryparams 改变才去触发,如果加上 params 会无限请求,直至浏览器卡死。

import useService  from '@/hooks/useService.ts';
import { IProps, IPagination, IQueryparamsItem } from './types';

const ComponentTable:FC<IProps> = ({columns,queryparams,queryurl,rowKey,...other}:IProps) => {
  const [pagination, setPagination] = useState<IPagination>({
    pageNo: 1,
    pageSize: 10
  })
  const [params, setParams] = useState<IQueryparamsItem>({
    ...pagination,
    ...queryparams,
  })
  const updateInPropsParams = useCallback(
    () => {
      setParams({
        ...params,
        ...queryparams
      });
      setPagination({
        ...pagination,
        pageNo: queryparams.pageNo!,
      })
    },
    [queryparams],
  )
  useEffect(() => {
    updateInPropsParams();
  }, [updateInPropsParams]);

  const { loading, response } = useService(queryurl, params); 
  const handlePageChange = (pageNo:number, pageSize?:number) => {
    setPagination({
      pageNo,
      pageSize: pageSize!
    })
    setParams({
      ...queryparams
    })
  }
  return (
    <div className="com-table">
      {
        other?.children
      }
      <Spin spinning={loading}>
        <Table 
          className="list-container"
          scroll={{ y: document.body.offsetHeight - 280 }} 
          rowKey={rowKey} 
          columns={columns()} 
          dataSource={response?.list} 
          pagination={false} 
        />
        <Pagination 
          defaultCurrent={pagination.pageNo} 
          onChange={(pageNo, pageSize) => handlePageChange(pageNo, pageSize)} 
          hideOnSinglePage 
          total={response?.pageTotal} />
      </Spin>
    </div>
  )
}
export default ComponentTable

再另附上 ComponentTable 组件的类型声明,大家可以去根据自己的项目更改。

import { ReactElement } from "react";
import { ColumnProps } from "antd/lib/table/interface";

export interface IPagination {
  pageNo?: number;
  pageSize?: number;
}
export interface IQueryparamsItem extends IPagination{
  keyword: string;
}

export interface IProps {
  children?: ReactElement;
  columns: () => Array<ColumnProps<any>>;
  queryparams: IQueryparamsItem;
  queryurl: (params:object)=>Promise<void>;
  rowKey: (record: any, index: number) => string
}

父组件

如果要在父组件中使用的话,直接引入,就像刚开始说的一样,传入columns,queryurl, queryparams 所需要的参数即可

import ComTable from './ComTable';
import {CloumnsProps} from './types';
import { queryurl } from '@/server/api';
import { IQueryparamsItem } from './ComTable/types.ts';
const { Search } = Input;

const ParentCom:React.FC = () => {
  const [params, setParams] = useState<IQueryparamsItem>({
    keyword: '',
  })
  
  const columns: CloumnsProps = useCallback(
    () => {
      return [
        {
          title: '列1',
          dataIndex: 'one',
        },
        {
          title: '列2',
          dataIndex: 'two',
        },
        {
          title: '列3',
          render: (item:any) => {
            return (
              <>
                <Icon 
                  className="columnsIcon" 
                  type="edit" 
                />
              </>
            )
          }
        },
      ]
    },
    []
  )
  const handleSearch = (val:string) => {
    setParams({
      keyword: val,
      pageNo: 1,
    })
  }
  return (
    <ComTable
        columns={()=> columns()} 
        rowKey={(d: any) => (d.id).toString()}
        queryparams={params}
        queryurl={getAccountList}
      >
        <Search style={{ width: 280 }} placeholder="手机号/用户名/昵称" onSearch={(val) => handleSearch(val)} enterButton />
    </ComTable>
  )
}

export default ParentCom

结尾

虽然功能很小,也希望可给大家一咻咻帮助。我旨在记录下自己的开发过程,顺便做个标记,方便自己以后想看的话,直接翻找。如果有问题,可以帮我指出来,或者有更好的想法,也提出来,我都会改进的~~~感谢!