Ant Design Pro如何实现三级嵌套可编辑表格及解决Hook报错问题
问题根因
Rendered more hooks than during the previous render 报错的核心原因是你在 expandedRowRender 回调里直接定义了使用 useState 的渲染函数,这类回调不是标准的React函数组件,React无法保证每次渲染时hooks的调用顺序和数量一致,违反了React Hooks的使用规则:
- 仅在React函数组件的最顶层调用Hook
- 仅在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
相关产品推荐
相关产品推荐

