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

Ant Design Pro如何实现三级嵌套可编辑表格及解决Hook报错问题

问题根因

Rendered more hooks than during the previous render 报错的核心原因是你在 expandedRowRender 回调里直接定义了使用 useState 的渲染函数,这类回调不是标准的React函数组件,React无法保证每次渲染时hooks的调用顺序和数量一致,违反了React Hooks的使用规则:

  1. 仅在React函数组件的最顶层调用Hook
  2. 仅在React函数组件或自定义Hook中调用Hook

解决方案

步骤1:把内层可编辑表格抽成独立的函数组件

把原来写在 expandedRowRender3 里的逻辑单独抽成合法的React组件,所有状态(editableKeys、dataSource)都放在这个独立组件内部维护。

// 抽成独立函数组件
const InnerEditableTable = () => {
  const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
  const [dataSource, setDataSource] = useState<DataSourceType[]>([]);

  const columns: ProColumns<DataSourceType>[] = [
    // 原有列配置保持不变
    {
      title: '活动名称',
      dataIndex: 'title',
      formItemProps: (form, { rowIndex }) => {
        return {
          rules: rowIndex > 2 ? [{ required: true, message: '此项为必填项' }] : [],
        };
      },
      editable: (text, record, index) => index !== 0,
      width: '30%',
    },
    {
      title: '状态',
      key: 'state',
      dataIndex: 'state',
      valueType: 'select',
      valueEnum: {
        all: { text: '全部', status: 'Default' },
        open: { text: '未解决', status: 'Error' },
        closed: { text: '已解决', status: 'Success' },
      },
    },
    {
      title: '描述',
      dataIndex: 'decs',
      fieldProps: (from, { rowKey, rowIndex }) => {
        if (from.getFieldValue([rowKey || '', 'title']) === '不好玩') return { disabled: true };
        if (rowIndex > 9) return { disabled: true };
        return {};
      },
    },
    {
      title: '活动时间',
      dataIndex: 'created_at',
      valueType: 'date',
    },
    {
      title: '操作',
      valueType: 'option',
      width: 200,
      render: (text, record, _, action) => [
        <a
          key="editable"
          onClick={() => action?.startEditable?.(record.id)}
        >
          编辑
        </a>,
      ],
    },
  ];

  return (
    <EditableProTable<DataSourceType>
      rowKey="id"
      recordCreatorProps={false}
      columns={columns}
      request={async () => ({
        data: defaultData,
        total: 3,
        success: true,
      })}
      value={dataSource}
      onChange={setDataSource}
      editable={{
        type: 'multiple',
        editableKeys,
        onSave: async (rowKey, data, row) => {
          console.log(rowKey, data, row);
          await waitTime(2000);
        },
        onChange: setEditableRowKeys,
      }}
    />
  );
};

步骤2:把第二层嵌套表格也抽成独立组件

同理,原有 expandedRowRender 逻辑也抽成独立组件 SecondLevelTable,在它的 expandedRowRender 里直接使用刚才抽好的 InnerEditableTable 组件:

const SecondLevelTable = () => {
  const data = [];
  for (let i = 0; i < 3; i += 1) {
    data.push({
      key: i,
      date: '2014-12-24 23:12:00',
      name: 'This is production name',
      upgradeNum: 'Upgraded: 56',
    });
  }

  return (
    <ProTable
      columns={[
        { title: 'Date', dataIndex: 'date', key: 'date' },
        { title: 'Name', dataIndex: 'name', key: 'name' },
        { title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
        {
          title: 'Action',
          dataIndex: 'operation',
          key: 'operation',
          valueType: 'option',
          render: () => [<a key="Pause">Pause</a>, <a key="Stop">Stop</a>],
        },
      ]}
      headerTitle={false}
      expandable={{ expandedRowRender: () => <InnerEditableTable /> }}
      search={false}
      options={false}
      dataSource={data}
      pagination={false}
    />
  );
};

步骤3:调整最外层表格的配置

最外层表格的 expandedRowRender 直接返回第二层组件即可:

export default () => {
  return (
    <ProTable<TableListItem>
      columns={columns}
      request={(params, sorter, filter) => {
        console.log(params, sorter, filter);
        return Promise.resolve({
          data: tableListDataSource,
          success: true,
        });
      }}
      rowKey="key"
      pagination={{ showQuickJumper: true }}
      expandable={{ expandedRowRender: () => <SecondLevelTable /> }}
      search={false}
      dateFormatter="string"
      headerTitle="嵌套表格"
      options={false}
    />
  );
};

补充说明

如果需要给内层表格传递父行的关联数据,直接通过组件props传递即可,比如 <InnerEditableTable parentRowId={record.id} />,独立组件支持正常的props传值,不会影响hooks的使用规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 06:18:03