You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React应用中可复用Datatable组件的正确实现方式咨询

React可复用Datatable组件构建方案

你的思路完全正确——通过传递columns、数据、操作逻辑等参数复用Datatable是React组件复用的标准做法,不仅不会导致逻辑复杂,反而能大幅减少代码重复、降低维护成本。以下是具体的改造步骤:

1. 重构通用Datatable组件

把组件中页面专属的内容全部改为通过props传入,只保留表格渲染、基础交互(如分页、排序)这类通用逻辑。修改后的组件示例:

const Datatable = ({ data, columns, onDelete }) => {
  // 可维护内部筛选/排序后的状态(根据需求选择)
  const [tableData, setTableData] = useState(data);

  // 通用删除逻辑:优先使用父组件传入的自定义逻辑,无则用默认本地删除
  const handleDelete = (id) => {
    if (onDelete) {
      onDelete(id);
    } else {
      setTableData(tableData.filter(item => item.id !== id));
    }
  };

  return (
    <div className="datatable">
      <table>
        <thead>
          <tr>
            {columns.map(col => (
              <th key={col.key}>{col.title}</th>
            ))}
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          {tableData.map(row => (
            <tr key={row.id}>
              {columns.map(col => (
                <td key={col.key}>
                  {/* 支持自定义列渲染,适配不同页面的特殊展示需求 */}
                  {col.render ? col.render(row) : row[col.key]}
                </td>
              ))}
              <td>
                <button onClick={() => handleDelete(row.id)}>删除</button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      {/* 通用分页、筛选组件等可在这里添加 */}
    </div>
  );
};

export default Datatable;

2. 在页面组件中传入专属参数

以EmployeeList为例,在页面中处理专属数据请求、列配置、操作逻辑,然后传递给Datatable:

import { useState, useEffect } from 'react';
import Datatable from '../components/Datatable';

const EmployeeList = () => {
  const [employeeData, setEmployeeData] = useState([]);

  // 页面专属:获取员工数据
  useEffect(() => {
    const fetchEmployees = async () => {
      const res = await fetch('/api/employees');
      const data = await res.json();
      setEmployeeData(data);
    };
    fetchEmployees();
  }, []);

  // 页面专属:员工列配置,支持自定义渲染(比如状态标签)
  const employeeColumns = [
    { key: 'id', title: '员工ID' },
    { key: 'name', title: '姓名' },
    { key: 'status', title: '状态', render: (row) => (
      <span className={row.status === 'active' ? 'active-tag' : 'inactive-tag'}>
        {row.status === 'active' ? '在职' : '离职'}
      </span>
    )},
    { key: 'department', title: '部门' }
  ];

  // 页面专属:删除员工的接口调用逻辑
  const handleEmployeeDelete = async (id) => {
    await fetch(`/api/employees/${id}`, { method: 'DELETE' });
    setEmployeeData(employeeData.filter(item => item.id !== id));
  };

  return (
    <div className="list">
      <Sidebar/>
      <div className="listContainer">
        <Navbar/>
        <Datatable 
          data={employeeData} 
          columns={employeeColumns} 
          onDelete={handleEmployeeDelete} 
        />
      </div>
    </div>
  )
}

export default EmployeeList;

3. 通用与专属逻辑的边界划分

  • 留在Datatable的通用逻辑:表格结构渲染、分页、排序、基础筛选、通用样式等。
  • 放在页面的专属逻辑:数据请求、特定列的自定义渲染、与后端交互的操作(如删除、编辑)、页面专属的状态管理等。

实践建议

  • 不用一开始过度抽象:先从传递data、columns、onDelete这些核心参数开始,后续根据需求逐步添加其他props(如onEdit、pageSize等)。
  • 若多个页面有重复配置(比如默认分页参数),可封装BaseDatatable预设通用配置,再在特定页面组件中扩展,但当前阶段无需急于做这件事。
  • 数据状态尽量由页面组件管理,除非需要跨多组件共享数据,再考虑全局状态工具。

内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 04:15:33