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

在React中使用Tanstack Table实现列编辑功能

Tanstack Table 实现可编辑列(Status下拉选择+Role文本编辑)

要实现指定列的编辑功能,核心是利用Tanstack Table的cell自定义渲染能力,结合React状态管理来修改表格数据。以下是具体实现方案:

1. 维护可编辑的数据状态

首先在表格组件中把硬编码的用户数据转为可修改的React状态,确保修改后能自动触发表格更新:

import { useState } from 'react';
import { useReactTable, flexRender } from '@tanstack/react-table';
import { columns } from './columns';
import { initialUsers } from './your-data-source'; // 你的硬编码数据文件
import { I_UserPublic } from 'app/models/user.types';

export default function UserTable() {
  // 存储可编辑的用户数据
  const [users, setUsers] = useState<I_UserPublic[]>(initialUsers);

  // 更新单条用户数据的方法
  const updateUser = (userId: string, field: keyof I_UserPublic, value: any) => {
    setUsers(prevUsers => 
      prevUsers.map(user => 
        user.id === userId ? { ...user, [field]: value } : user
      )
    );
    // 这里可添加后端API调用,同步修改到服务器
  };

  // 创建表格实例,给可编辑列添加渲染逻辑
  const table = useReactTable({
    data: users,
    columns: columns.map(column => {
      // Status列:下拉选择编辑
      if (column.accessorKey === 'status') {
        return {
          ...column,
          cell: ({ row }) => {
            const user = row.original;
            // 自定义状态选项,按需调整
            const statusOptions = ['Active', 'Inactive', 'Pending'];
            return (
              <select
                value={user.status}
                onChange={(e) => updateUser(user.id, 'status', e.target.value)}
                className="border rounded px-2 py-1 text-sm"
              >
                {statusOptions.map(opt => (
                  <option key={opt} value={opt}>{opt}</option>
                ))}
              </select>
            );
          }
        };
      }

      // Role列:文本编辑(处理string[]类型)
      if (column.accessorKey === 'role') {
        return {
          ...column,
          cell: ({ row }) => {
            const user = row.original;
            // 把数组转成逗号分隔的字符串方便编辑
            const roleText = user.role.join(', ');
            return (
              <input
                type="text"
                value={roleText}
                onChange={(e) => {
                  // 把输入的字符串转回数组,过滤空值
                  const newRoles = e.target.value
                    .split(',')
                    .map(r => r.trim())
                    .filter(r => r.length > 0);
                  updateUser(user.id, 'role', newRoles);
                }}
                className="border rounded px-2 py-1 text-sm w-full"
              />
            );
          }
        };
      }

      return column;
    }),
  });

  // 渲染表格(适配现有样式)
  return (
    <div className="overflow-x-auto">
      <table className="min-w-full divide-y divide-gray-200">
        <thead className="bg-gray-50">
          {table.getHeaderGroups().map(headerGroup => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map(header => (
                <th key={header.id} className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                  {flexRender(header.column.columnDef.header, header.getContext())}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody className="bg-white divide-y divide-gray-200">
          {table.getRowModel().rows.map(row => (
            <tr key={row.id}>
              {row.getVisibleCells().map(cell => (
                <td key={cell.id} className="px-6 py-4 whitespace-nowrap">
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

2. 可选:直接在列定义中配置编辑逻辑

如果不想在表格组件中修改列数组,可以直接在columns.ts中给目标列添加cell渲染逻辑,通过表格的meta传递更新方法:

修改columns.ts:

import { ColumnDef } from '@tanstack/react-table';
import { I_UserPublic } from 'app/models/user.types';
import Link from 'next/link';

export const columns: ColumnDef<I_UserPublic, { updateUser: (userId: string, field: keyof I_UserPublic, value: any) => void }>[] = [
  // ...其他列保持不变...
  {
    accessorKey: "role",
    header: ({ column }) => {
      return (
        <span className="flex justify-between">
          User Role
          <Link className="" href="#" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
            <svg className="w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
              <path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 4v16M7 4l3 3M7 4 4 7m9-3h6l-6 6h6m-6.5 10 3.5-7 3.5 7M14 18h4"/>
            </svg>
          </Link>
        </span>
      )
    },
    cell: ({ row, table }) => {
      const user = row.original;
      const roleText = user.role.join(', ');
      return (
        <input
          type="text"
          value={roleText}
          onChange={(e) => {
            const newRoles = e.target.value.split(',').map(r => r.trim()).filter(r => r);
            table.options.meta?.updateUser(user.id, 'role', newRoles);
          }}
          className="border rounded px-2 py-1 text-sm w-full"
        />
      );
    },
  },
  {
    accessorKey: "status",
    header: ({ column }) => {
      return (
        <span className="flex justify-between">
          User Status
          <Link className="" href="#" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
            <svg className="w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
              <path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 4v16M7 4l3 3M7 4 4 7m9-3h6l-6 6h6m-6.5 10 3.5-7 3.5 7M14 18h4"/>
            </svg>
          </Link>
        </span>
      )
    },
    cell: ({ row, table }) => {
      const user = row.original;
      const statusOptions = ['Active', 'Inactive', 'Pending'];
      return (
        <select
          value={user.status}
          onChange={(e) => table.options.meta?.updateUser(user.id, 'status', e.target.value)}
          className="border rounded px-2 py-1 text-sm"
        >
          {statusOptions.map(opt => (
            <option key={opt} value={opt}>{opt}</option>
          ))}
        </select>
      );
    },
  },
];

表格组件中传递meta:

const table = useReactTable({
  data: users,
  columns,
  meta: { updateUser }, // 传递更新方法
});

3. 关键注意事项

  • 状态驱动更新:必须将用户数据放到React state中,否则修改后表格不会重新渲染。
  • 类型匹配:role字段是string[]类型,编辑时要做好字符串和数组的转换,避免类型错误。
  • 样式适配:可根据Flowbite的样式规范调整输入框和下拉框的className,保持和现有UI一致。
  • 后端同步:如果需要持久化修改,在updateUser方法中添加API调用即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:42:02