通过hook封装表格常用功能(react)

封装内容

  • 配置分页:usePagination
  • 生成过滤项:useFilter
  • 获取表格选择配置:useSelect
  • 生成批量删除按钮:useDelete
  • 生成模态框:useModal

基于react@18.2.0,antd@5.12.5

示例

render部分:

jsx 复制代码
<React.Fragment>
    <Form layout="inline">
    {DeleteEle}
    {FilterEles}
    </Form>
    <Table
    {...{
        columns,
        dataSource: list || [],
        rowSelection,
        pagination: paginationConfig,
        rowKey: 'inform_uuid',
    }}
    />
    {ModalEle}
</React.Fragment>

效果:

完整代码

js 复制代码
import React, { useState, useEffect } from 'react';
import { Form, Table, Tag } from 'antd';
import Detail from './Detail'; // 详情弹框的内容
import { LIST_RES } from '../store'; // 模拟请求数据
import TableGenerator from '../TableGenerator'; // 封装的钩子

const { useFilter, usePagination, useSelect, useDelete, useModal } =
   TableGenerator;

const TestTable = () => {
   // 1. 生成过滤项。
   // 传入配置,可以自行扩展。目前支持输入框、日期范围选择器、普通选择器
   // FilterEles:过滤项表单项
   // filterRefresh:暴露出来告诉外层需要重新获取数据了
   const { FilterEles, filterRefresh } = useFilter([
      {
         name: 'name', // 字段
         label: '平台名称', // placeholder
         type: 'inputString', // 组件类型:字符串输入框
      },
      {
         name: 'status',
         label: '状态',
         type: 'select', // 组件类型:字符串输入框
         initValue: 0, // 默认数据(可选)
         options: [
            { value: 0, label: '全部状态' },
            { value: 1, label: '成功' },
            { value: 2, label: '失败' },
         ],
      },
      {
         name: ['start_date', 'end_date'],
         label: ['开始日期', '结束日期'],
         type: 'dateRange', // 组件类型:日期选择器
      },
   ]);

   // 2. 获取分页配置。
   // pagination:{page,pageSize,total}
   // pageRefresh:暴露出来告诉外层需要重新获取数据了
   // paginationConfig:作为表格的分页属性
   const { pagination, setPagination, pageRefresh, paginationConfig } =
      usePagination();

   // 3. 表格选中配置
   // selectedKeys: Array<string>
   // rowSelection: 作为表格的选择属性
   const { selectedKeys, setSelectedKeys, rowSelection } = useSelect();

   // 4. 配置模态框。
   // 传入数组,用于配置所有弹框类型(详情、添加、编辑等),目前只支持详情,可自行扩展
   // ModalEle: 模态框组件
   // modal: {isShow:boolean, action:string, record:any}
   const { ModalEle, modal, setModal } = useModal([
      {
         title: '详情', // 弹框标题
         action: 'detail', // 弹框类型
         getComponent: (record) => <Detail data={record} />, // 弹框数据
         // ... 可以继续添加属性,其他属性将会应用到Modal中 比如footer、handleOk
      },
   ]);

   // 5. 批量删除按钮,需要搭配useSelect使用
   const [DeleteEle] = useDelete({
      url: 'ajax/deleteItem.json',
      data: {
         uuids: selectedKeys,
      },
      success: () => {  // 可选,删除成功后还需要调用的内容
         setSelectedKeys([]);
         getList();
      },
      keys: selectedKeys, // 用于判断按钮是否可点击
   });

   const [list, setList] = useState([]);
   const getList = () => {
      // 请求数据
      const data = LIST_RES.data;
      setList(data.list);
      setPagination({
         ...pagination,
         page: data.page,
         total: data.total,
      });
   };
   // 触发刷新:修改page、pageSize、点击查询、重置时将会触发
   useEffect(() => {
      getList();
   }, [pageRefresh, filterRefresh]);

   const columns = [
      {
         title: '序号',
         width: 50,
         align: 'center',
         render: (v, r, index) =>
            index + 1 + (pagination.page - 1) * pagination.pageSize,
      },
      {
         title: '平台名称',
         width: 180,
         dataIndex: 'req_psname',
      },
      {
         title: '请求时间',
         width: 140,
         dataIndex: 'inform_time',
      },
      {
         title: '通知对象',
         width: 180,
         dataIndex: 'inform_pnumbers',
         render: (v) => v.join(', '),
      },
      {
         title: '通知内容',
         width: 200,
         dataIndex: 'inform_content',
      },
      {
         title: '通知结果',
         width: 80,
         dataIndex: 'inform_result',
         render: (v) =>
            v === 0 ? (
               <Tag color="#87d068">成功</Tag>
            ) : (
               <Tag color="#f50">失败</Tag>
            ),
      },
      {
         title: '操作',
         width: 60,
         render: (v, record) => (
            <React.Fragment>
               <a
                  onClick={() => {
                     setModal({
                        action: 'detail',
                        isShow: true,
                        record,
                     });
                  }}
               >
                  详情
               </a>
            </React.Fragment>
         ),
      },
   ];

   return (
      <React.Fragment>
         <Form layout="inline">
            {DeleteEle}
            {FilterEles}
         </Form>
         <Table
            {...{
               scroll: { x: 1170, y: '70vh' },
               columns,
               dataSource: list || [],
               rowSelection,
               pagination: paginationConfig,
               rowKey: 'inform_uuid',
            }}
         />
         {ModalEle}
      </React.Fragment>
   );
};

export default TestTable;

Hook封装

usePagination

配置表格的分页功能

js 复制代码
const usePagination = () => {
    // 默认数据
  const [pagination, setPagination] = useState({
    page: 1,
    pageSize: 10,
    total: 0,
  });
  // 用于触发外层的刷新
  const [pageRefresh, setPageRefresh] = useState(false);

  const config = {
    size: "small",
    showQuickJumper: false,
    total: pagination.total,
    current: pagination.page,
    showSizeChanger: true,
    onChange: (current) => {
      setPagination({
        ...pagination,
        page: current,
      });
      setPageRefresh(() => !pageRefresh); // 修改页数后,通知外层刷新
    },
    pageSize: pagination.pageSize,
    onShowSizeChange: (page, pageSize) => {
      setPagination({
        page,
        pageSize,
      });
      setPageRefresh(() => !pageRefresh);// 修改页码后,通知外层刷新
    },
  };

  return {
    pagination,
    pageRefresh,
    setPagination,
    paginationConfig: config,
  };
};

useSelect

配置表格的选择功能

js 复制代码
const useSelect = () => {
  const [selectedKeys, setSelectedKeys] = useState([]);
  const rowSelection = {
    onChange: (selectedKeys) => {
      setSelectedKeys(selectedKeys);
    },
    selectedRowKeys: selectedKeys,
  };

  return { selectedKeys, rowSelection, setSelectedKeys };
};

useDelete

配置表格的批量删除按钮

js 复制代码
const useDelete = ({ url = "", data = {}, success = null, keys = [] }) => {
  const handleDel = () => {
    // 请求..
    message.success("删除成功");
    success && success();  // 
  };

  const DeleteEle = (
    <React.Fragment>
        { /**OPER.isDelete  : 当有权限时才显示  */}
      {OPER.isDelete && (
        <Form.Item>
          {keys.length === 0 ? (
            <Button icon={<DeleteOutlined />} size="small" disabled>
              批量删除
            </Button>
          ) : (
            <Popconfirm
              title="批量删除"
              description="你确定要删除吗?"
              okText="确定"
              cancelText="取消"
              onConfirm={handleDel}
            >
              <Button
                type="primary"
                danger
                icon={<DeleteOutlined />}
                size="small"
              >
                批量删除
              </Button>
            </Popconfirm>
          )}
        </Form.Item>
      )}
    </React.Fragment>
  );

  return [DeleteEle];
};

useFilter

配置过滤项,附带查询、重置按钮

js 复制代码
import { initFilter, getFormItem } from "./utils";  // 根据配置配置初始化filter数据 ;  根据配置获取组件

const useFilter = (config = []) => {
  const [filter, setFilter] = useState(initFilter(config));
  const [refresh, setRefresh] = useState(false);

 // 重置过滤项
  const handleResetFilter = () => {
    setFilter(initFilter(config));
    setRefresh(!refresh); // 通知外层刷新
  };

  // 触发查询
  const handleSearch = () => {
    setRefresh(!refresh); // 通知外层刷新
  };

 // 修改日期类型
  const handleDateChange = ([startField, endField], [startTime, endTime]) => {
    setFilter({ ...filter, [startField]: startTime, [endField]: endTime });
  };

 // 修改input 类型
  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFilter({ ...filter, [name]: value });
  };

// 修改select 类型
  const handleSelectChange = (name, value) => {
    setFilter({ ...filter, [name]: value });
  };


 // 根据配置生成表单项
  const FormItemEles = getFormItem(config, {
    filter,
    handleInputChange,
    handleSelectChange,
    handleDateChange,
  });
 

  const HandlerEles = [
    <Form.Item>
      <Button
        type="primary"
        size="small"
        onClick={handleSearch}
        icon={<SearchOutlined />}
      >
        查询
      </Button>
    </Form.Item>,
    <Form.Item>
      <Button
        type="primary"
        size="small"
        onClick={handleResetFilter}
        icon={<ReloadOutlined />}
      >
        重置
      </Button>
    </Form.Item>,
  ];

  return {
    filter,
    FilterEles: [...FormItemEles, ...HandlerEles],
    filterRefresh: refresh,
  };
};

initFilter:根据不同类型初始化filter数据

jsx 复制代码
const initFilter = (arr) => {
  const obj = {};
  arr.forEach((item) => {
    switch (item.type) {
      case "inputString":
        obj[item.name] = item.initValue || "";
        break;
      case "select":
        obj[item.name] = item.initValue || 0;
        break;
      case "dateRange":
        item.name.forEach((name) => {
          obj[name] = "";
        });
        break;
      default:
        break;
    }
  });

  return obj;
};

getFormItem:根据不同的类型生成表单项组件

jsx 复制代码
const getFormItem = (
  arr,
  { filter, handleInputChange, handleSelectChange, handleDateChange }
) => {
  const Eles = [];
  arr.forEach((item) => {
    const name = item.name;
    const label = item.label;
    const value = filter[name];
    switch (item.type) {
      case "inputString":
        Eles.push(
          <Form.Item>
            <Input
              size="small"
              placeholder={label}
              name={name}
              value={value}
              onChange={handleInputChange}
            />
          </Form.Item>
        );
        break;
      case "select":
        Eles.push(
          <Form.Item>
            <Select
              size="small"
              style={{ width: 120 }}
              onChange={(v) => {
                handleSelectChange(name, v);
              }}
              name={name}
              value={value}
              options={item.options}
            />
          </Form.Item>
        );
        break;
      case "dateRange":
        Eles.push(
          <Form.Item>
            <RangePicker
              onChange={(dates) => {
                handleDateChange(name, dates);
              }}
              value={[filter[name[0]], filter[name[1]]]}
              placeholder={label}
              size="small"
            />
          </Form.Item>
        );
      default:
        break;
    }
  });

  return Eles;
};

useModal

配置模态框

js 复制代码
const useModal = (
  config = [
    {
      title: "详情",
      action: "detail",
      getComponent: (record)=><div>详情</div>,
    },
  ]
) => {
  const [modal, setModal] = useState({
    isShow: false,
    record: null,
    action: "",
  });
  const handleCancel = () => {
    setModal({
      isShow: false,
      record: null,
      action: "",
    });
  };

  //对话框信息
  let modalProps = {
    open: modal.isShow,
    onCancel: handleCancel,
    maskClosable: false,
    footer: null,
  };

  const currentModal = config.find((item) => item.action === modal.action); // 找到当前模态框数据
  if (currentModal) {
    const { action, getComponent, ...other } = currentModal;
    // 配置传进来的其余属性
    modalProps = {
      ...modalProps,
      ...other,
    };
  }

  const ModalEle = (
    <Modal width="80%" {...modalProps}>
      {currentModal && currentModal.getComponent(modal.record)}
    </Modal>
  );

  return { ModalEle, modal, setModal };
};

源码

后记:起因是写表格写烦了,于是快速封装了一个,因为仅针对于当前的需求,所以写的比较简单,还有很多地方需要扩展。如果有其他思路,还请多多赐教~

相关推荐
掘金者阿豪33 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen1 小时前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员2 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger3 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4533 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css