react中hook封装一个table组件 与 useColumns组件

目录

  • 1:react中hook封装一个table组件
    • 依赖
    • CommonTable / index.tsx
    • 使用组件
    • 效果
  • 2:useColumns组件
    • useColumns.tsx
    • 使用

1:react中hook封装一个table组件

依赖

cnpm i react-resizable --save
cnpm i ahooks
cnpm i --save-dev @types/react-resizable

CommonTable / index.tsx

import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react';
import { createUseStyles } from 'react-jss';
import { Resizable } from 'react-resizable';
import { ColumnType } from 'antd/lib/table';
import { Table, Button } from 'antd';
import type { ButtonProps, TableProps } from 'antd';
import { useSize } from 'ahooks';

export interface ICommonTableProps<RecordType> extends TableProps<RecordType> {
  onCreate?: () => void;
  onEdit?: () => void;
  deleteBtn?: {
    props?: ButtonProps;
    onDelete?: () => void;
  };
  isDrag?: boolean; // 控制table是否展示 可拖拽的表头
}
const useCommonTableStyles = createUseStyles({
  wrapper: {
    background: '#fff',
    marginTop: '12px',
    padding: '12px 12px'
  },
  header: {
    display: 'flex',
    marginTop: '8px',
    marginBottom: '20px'
  },
  tablActions: {
    display: 'flex',
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center'
  },
  headerBtn: {
    marginLeft: '16px'
  },
  resizableHandle : {
    position: 'absolute',
    right: '-5px',
    bottom: 0,
    zIndex: 1,
    width: '10px',
    height: '100%',
    cursor: 'col-resize'
  }
});

// 表头拖拽组件
const ResizableTitle = (props: any ) => {
const { onResize, width, ...restProps } = props
  const classes = useCommonTableStyles();
  if (!width) { return (<th {...restProps} />) };
  return (
    <Resizable
      width={parseInt(width)}
      height={0}
      handle={
        <span className={classes.resizableHandle} onClick={e => { e.stopPropagation() }} />
      }
      onResize={onResize}
      draggableOpts={{ enableUserSelectHack: false }}
    >
      <th {...restProps} style={{ ...restProps?.style, userSelect: 'none' }} />
    </Resizable>
  );
};

export const CommonTable = <RecordType extends Record<string, any> = any>(
  props: ICommonTableProps<RecordType>
) => {
  const { onCreate, onEdit, deleteBtn, isDrag = true } = props;
  const classes = useCommonTableStyles();
  const wrapperRef = useRef<HTMLDivElement>(null);
  const bodyRef = useRef(document.body);
  const size = useSize(bodyRef);
  const [scroll, setScroll] = useState<TableProps<any>['scroll']>({ x: 'max-content' });

  const [rescolumns, setResColumns] = useState<ColumnType<RecordType>[]>(props.columns || []);

  const handleResize = (index: number): ((_: any, Resize: { size: { width: any } }) => void) => {

    return (_: any, Resize: { size: { width: any; }; }) => {
      const temp = [...rescolumns];
      temp[index] = { ...temp[index], width: Resize.size.width };
      setResColumns(temp);
    };
  };

  // 把 columns 用 map 重组为支持可拖拽的cell
  const columnsMap: any[] = useMemo(() => {
    return (
      rescolumns?.map((column:any,index:any) => ({
        ...column,
        onHeaderCell: (col: { width: any; }) => ({ width: col.width, onResize: handleResize(index) }),
        title: column.title,
      })) || []
    );
  }, [rescolumns]);

  useEffect(() => {
    if (wrapperRef.current) {
      const { top } = wrapperRef.current?.getBoundingClientRect();
      setScroll({
        x: 'max-content',
        y: innerHeight - top - 210
      });
    }
  }, [wrapperRef, size]);

  return (
    <div className={classes.wrapper} ref={wrapperRef}>
      <div className={classes.header}>
        <div className={classes.tablActions}>
          {onCreate && (
            <Button className={classes.headerBtn} type='primary' onClick={onCreate}>
              新增
            </Button>
          )}
          {onEdit && (
            <Button className={classes.headerBtn} type='default'>
              编辑
            </Button>
          )}
          {deleteBtn && (
            <Button
              {...deleteBtn.props}
              className={classes.headerBtn}
              type='default'
              danger
              onClick={deleteBtn.onDelete}
            >
              删除
            </Button>
          )}
        </div>
      </div>
      <Table 
        scroll={scroll} 
        {...props} 
        components={isDrag ? { header: { cell: ResizableTitle } } : undefined}
        columns={columnsMap}
      />
    </div>
  );
};

使用组件

import { createUseStyles } from 'react-jss';
import type { TableRowSelection } from 'antd/lib/table/interface';
import type { ColumnsType } from 'antd/lib/table';
import { useEffect, useMemo, useState, useRef } from 'react';
import { CommonTable } from '../components/CommonTable/index';

const useStyles = createUseStyles({
  table: {
    background: '#fff',
    padding: '16px',
    marginTop: '16px',
    width: '100%',
  },
  textBtn: {
    color: '#0068FF',
    cursor: 'pointer',
    userSelect: 'none',
  },
});
const TablePage = () => {
  const [tableData, setTableData] = useState<any>([]);
  const [currentPage, setCurrentPage] = useState<number>(1);
  const [currentSize, setCurrentSize] = useState<number>(20);
  const classes = useStyles();
  const [tableLoading, setTableLoading] = useState(false);
  const [tableDataTotal, setTableDataTotal] = useState(0);
  const [selectedRow, setSelectedRow] = useState([] as any); //控制表格是否已选

  useEffect(() => {
    const resTable = [
      { id: 1, type: 1, status: '草稿' },
      { id: 2, type: 0, status: '已完成' },
      { id: 3, type: 1, status: '草稿' },
    ];
    setTableData(resTable);
  }, []);

  const rowSelection: TableRowSelection<any> = {
    onChange: (selectedRowKeys, selectedRows) => {
      setSelectedRow(selectedRowKeys);
    },
  };
  // 分页
  const handlePageChange = (page: number, size: number) => {
    setCurrentPage(page);
    setCurrentSize(size);
    // getList({ page, size, param: queryData }); // 获取table数据
  };
  const tableColumns: ColumnsType<any> = useMemo(() => {
    return [
      {
        title: '操作',
        dataIndex: 'code',
        fixed: 'left',
        width: '100px',
        render: (text, record) => (
          <div
            className={classes.textBtn}
            onClick={() => {
              console.log('onClick', text,"record",record);
            }}
          >
            {record['status'] === '草稿' ? '编辑' : '查看'}
          </div>
        ),
      },
      {
        title: '序号',
        dataIndex: 'id',
        width: '60px',
        render: (_, __, index) => index + 1 + (currentPage - 1) * currentSize,
      },
      {
        title: '来源',
        dataIndex: 'type',
        // width: '130px', // 最后一个宽度不传
        render: (_, __, index) => (_ === 1 ? '系统' : '手工'),
      },
    ];
  }, [classes.textBtn, currentPage, currentSize]);

  return (
    <>
      <CommonTable
        rowKey={'id'}
        className={classes.table}
        columns={tableColumns}
        scroll={{
          x: 'max-content',
        }}
        pagination={{
          showTotal: () => `${tableDataTotal} 条记录`,
          onChange: (page, size) => handlePageChange(page, size),
          hideOnSinglePage: false,
          showQuickJumper: true,
          showSizeChanger: true,
          current: currentPage,
          pageSize: currentSize,
          total: tableDataTotal,
        }}
        dataSource={tableData}
        loading={tableLoading}
        rowSelection={rowSelection}
      />
      <CommonTable
        rowKey={'id'}
        isDrag={false}
        className={classes.table}
        columns={tableColumns}
        scroll={{
          x: 'max-content',
        }}
        pagination={{
          showTotal: () => `${tableDataTotal} 条记录`,
          onChange: (page, size) => handlePageChange(page, size),
          hideOnSinglePage: false,
          showQuickJumper: true,
          showSizeChanger: true,
          current: currentPage,
          pageSize: currentSize,
          total: tableDataTotal,
        }}
        dataSource={tableData}
        loading={tableLoading}
        rowSelection={rowSelection}
      />
    </>
  );
};
export default TablePage;

效果

在这里插入图片描述

2:useColumns组件

useColumns.tsx

import React, { useMemo, useCallback } from 'react';

interface Args {
  handleEdit: (r: any) => void;
  handleSeeDetail: (r: any) => void;
}

export const useColumns = ({ handleEdit, handleSeeDetail }: Args) => {
  const handleEvent = useCallback(
    (v: string, record: any, e) => {
      e.stopPropagation();
      if (v === '编辑') {
        handleEdit(record);
      } else {
        handleSeeDetail(record);
      }
    },
    [handleSeeDetail, handleEdit],
  );

  // 渲染查看 || 编辑
  const showPop = useCallback(
    (record: any) => {
      if (record.status === '1') {
        return (
          <span
            onClick={(e) => handleEvent('编辑', record, e)}
            className="check-btn"
          >
            编辑
          </span>
        );
      } else {
        return (
          <span
            onClick={(e) => handleEvent('查看', record, e)}
            className="check-btn"
          >
            查看
          </span>
        );
      }
    },
    [handleEvent],
  );

  const columnsData: any = useMemo(() => {
    const columns = [
      {
        title: '操作',
        width: '100px',
        fixed: 'left',
        render: (_: any, record: any) => showPop(record),
      },
      {
        title: '序号',
        width: '60px',
        render: (_: any, record: any, index: number) => `${index + 1}`,
      },
      {
        title: '名称',
        dataIndex: 'name',
        width: '130px',
      },
      {
        title: '年龄',
        dataIndex: 'age',
      },
    ];
    return columns;
  }, [showPop]);


  return columnsData;
};

export default useColumns;

使用

import { createUseStyles } from 'react-jss';
import type { TableRowSelection } from 'antd/lib/table/interface';
import type { ColumnsType } from 'antd/lib/table';
import { useEffect, useMemo, useState, useRef } from 'react';
import { CommonTable } from '../components/CommonTable/index';
import useColumns from "./useColumns"

const useStyles = createUseStyles({
  table: {
    background: '#fff',
    padding: '16px',
    marginTop: '16px',
    width: '100%',
  },
  textBtn: {
    color: '#0068FF',
    cursor: 'pointer',
    userSelect: 'none',
  },
});
const TablePage = () => {
  const testData = [
    { name: '测试1',age: 10 },
    { name: '测试1',age: 20 },
    { name: '测试1',age: 30 },
  ]
  const handleSeeDetail = (row:any) => {
    console.log('查看', row);
    
  }
  const handleEdit = (row:any) => {
    console.log('编辑', row);
  }
  return (
    <>
      <CommonTable
        rowKey={'id'}
        className={classes.table}
        dataSource={testData}
        scroll={{
          x: 'max-content',
        }}
        columns={useColumns({ handleSeeDetail, handleEdit })}
      />
    </>
  );
};
export default TablePage;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/382450.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端JavaScript篇之对象创建的方式有哪些?

目录 对象创建的方式有哪些&#xff1f;1. 工厂模式&#xff1a;2. 构造函数模式&#xff1a;3. 原型模式&#xff1a;4. 混合模式&#xff1a;5. 动态原型模式&#xff1a;6. 寄生构造函数模式&#xff1a;7. 字面量方式&#xff1a; 对象创建的方式有哪些&#xff1f; JavaS…

elasticsearch增删改查

一、数据类型 1、字符串类型 &#xff08;1&#xff09;text &#xff08;2&#xff09;keyword 2、数值类型 &#xff08;1&#xff09;long、integer、short、byte、float、double 3、日期类型 &#xff08;1&#xff09;date 4、布尔类型 &#xff08;1&#xff0…

【Dubbo源码二:Dubbo服务导出】

入口 Dubbo服务导出的入口&#xff1a;服务导出是在DubboBootstrapApplicationListener在监听到ApplicationContextEvent的ContextRefreshedEvent事件后&#xff0c;会触发dubboBootstrap.start(), 在这个方法中最后会导出Dubbo服务 DubboBootstrapApplicationListener Dub…

指针的学习3

目录 字符指针变量 数组指针变量 二维数组传参的本质 函数指针变量 函数指针变量的创建 函数指针变量的使用 两段有趣的代码 typedef关键字 函数指针数组 转移表 回调函数&#xff1a; 字符指针变量 int main() {char arr[10] "abcdef";char* p1 arr;//…

机器学习2---逻辑回归(基础准备)

逻辑回归是基于线性回归是直线分的也可以做多分类 ## 数学基础 import numpy as np np.pi # 三角函数 np.sin() np.cos() np.tan() # 指数 y3**x # 对数 np.log10(10) np.log2(2) np.e np.log(np.e) #ln(e)# 对数运算 # log(AB) log(A) logB np.log(3*4)np.log(3)np.log(4) #…

vue electron 应用在windows系统上以管理员权限打开应用

打开package.json文件&#xff0c;在build下的win增加配置 "requestedExecutionLevel": "requireAdministrator",

前端JavaScript篇之对闭包的理解

目录 对闭包的理解用途循环中使用闭包解决 var 定义函数的问题 对闭包的理解 闭包是指一个函数能够访问并操作其词法作用域&#xff08;定义时所在的作用域&#xff09;之外的变量的能力。它可以通过在一个函数内部创建另一个函数来实现。内部函数可以访问外部函数的局部变量、…

6.0 Zookeeper session 基本原理详解教程

客户端与服务端之间的连接是基于 TCP 长连接&#xff0c;client 端连接 server 端默认的 2181 端口&#xff0c;也就 是 session 会话。 从第一次连接建立开始&#xff0c;客户端开始会话的生命周期&#xff0c;客户端向服务端的ping包请求&#xff0c;每个会话都可以设置一个…

HTML5+CSS3+移动web——HTML 基础

目录 一、标签语法 HTML的基本框架 1. 标题标签 2. 段落标签 3. 换行和水平线 4. 文本格式化标签 5. 图像标签 6. 路径 相对路径 绝对路径 7. 超链接标签 8. 音频 9. 视频 10. 注释 二、标签结构 一、标签语法 HTML 超文本标记语言——HyperText Markup Langua…

《动手学深度学习(PyTorch版)》笔记8.3

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

计算机网络——07协议层次及服务模型

协议层次及服务模型 协议层次 网络是一个复杂的系统 网络功能复杂&#xff1a;数字信号的物理信号承载、点到点、路由、rdt、进程区分、应用等现实来看&#xff0c;网络的许多构成元素和设备&#xff1a; 主机路由器各种媒体的链路应用协议硬件&#xff0c;软件 问题是&am…

单片机学习路线(简单介绍)

学习单片机对于电子爱好者和未来的嵌入式系统工程师来说是一段激动人心的旅程。单片机因其强大的功能、灵活性以及在各种智能设备中的广泛应用&#xff0c;成为了电子和计算机科学领域一个不可或缺的组成部分。如果你对如何开始这段旅程感到好奇&#xff0c;那么你来对地方了。…

Sqlite3安装步骤

1、Sqlite3以下载文件&#xff0c;配置环境变量的方式进行安装。 2、下方链接为官方的下载地址。 sqlite下载地址 2.1、需要两个下载文件&#xff0c;解压后将他们放在一起&#xff0c;假设解压后的路径为E:\sqlite。 sqlite-dll-win-x64-3450100.zip sqlite-tools-win-x6…

什么是CDR数字音频广播

一、什么是数字音频广播 CDR(China DigilalRadio)&#xff0c;即中国数字音领广播&#xff0c;是运用广播数字化技术&#xff0c;通过对音领信号进行信源编码、信道编码和载波调制传输&#xff0c;来实现数字音频广播业务和数据业务的播出。CDR与传统的FM调频广播相比&#xff…

【蓝桥杯冲冲冲】k 短路 / [SDOI2010] 魔法猪学院

蓝桥杯备赛 | 洛谷做题打卡day33 文章目录 蓝桥杯备赛 | 洛谷做题打卡day33题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示数据规模数据更新日志 题解代码我的一些话 【模板】k 短路 / [SDOI2010] 魔法猪学院 题目背景 注&#xff1a;对于 k k k 短路问…

第9章 智能租房——详情页

学习目标 掌握详情页房源数据展示功能的逻辑&#xff0c;能够实现在详情页上展示基本信息和配套设施 了解数据可视化&#xff0c;能够说出数据可视化的概念和流程 熟悉ECharts的用法和配置项&#xff0c;能够通过ECharts绘制常用图表&#xff0c;并为图表添加配置项 掌握户型…

2024年 前端JavaScript入门到精通 第一天

主要讲解JavaScript核心知识&#xff0c;包含最新ES6语法&#xff0c;从基础到API再到高级。让你一边学习一边练习&#xff0c;重点知识及时实践&#xff0c;同时每天安排大量作业&#xff0c;加深记忆&#xff0c;巩固学习成果。 1.1 基本软件与准备工作 1.2 JavaScript 案例 …

前端开发_AJAX基本使用

AJAX概念 AJAX是异步的JavaScript和XML(Asynchronous JavaScript And XML)。 简单点说&#xff0c;就是使用XMLHttpRequest对象与服务器通信。 它可以使用JSON&#xff0c;XML&#xff0c;HTML和text文本等格式发送和接收数据。 AJAX最吸引人的就是它的“异步"特性&am…

python-分享篇-GUI界面开发-PyQt5-对QListWidget表格进行数据绑定

代码 # -*- coding: utf-8 -*-# Form implementation generated from reading ui file bindtable.ui # # Created by: PyQt5 UI code generator 5.11.3 # # WARNING! All changes made in this file will be lost! 对QTableWidget表格进行数据绑定from PyQt5 import QtCore, Q…

Wireshark不显示Thrift协议

使用Wireshark对thrift协议进行抓包&#xff0c;但是只显示了传输层的tcp协议&#xff1a; "右键" -> "Decode As" 选择thrift的tcp端口 将“当前”修改为Thrift&#xff0c;然后点击“确定” 设置后&#xff0c;可以发现Wireshark里面显示的协议从Tcp变…