mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-16 01:26:07 +00:00
fix(frontend): key the node table by the computed row key, not id
The desktop node table used rowKey="id", but transitive sub-nodes (the
read-only rows surfaced from downstream nodes) all carry id 0, so a topology
with two or more transitive rows gave React duplicate keys. antd's rowKey
prop overrides the row object's own computed `key` (`t-${guid}` for
transitive rows, the numeric id otherwise), so the unique key the code
already builds was ignored — causing row-state/DOM mis-association on any
re-render (heartbeat refetch, address-eye toggle). The mobile card path
already keyed by record.key.
Key the table by "key" so transitive rows get their distinct t-${guid}
identity; direct nodes keep key === id, so row selection (filtered to numeric
keys) is unchanged.
This commit is contained in:
@@ -650,7 +650,7 @@ export default function NodeList({
|
||||
loading={loading}
|
||||
scroll={{ x: 'max-content' }}
|
||||
size="middle"
|
||||
rowKey="id"
|
||||
rowKey="key"
|
||||
rowSelection={dataSource.length > 1 ? {
|
||||
selectedRowKeys: selectedIds,
|
||||
onChange: (keys) => onSelectionChange(keys.filter((k) => typeof k === 'number') as number[]),
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
|
||||
import NodeList from '@/pages/nodes/NodeList';
|
||||
import type { NodeRecord } from '@/schemas/node';
|
||||
|
||||
import { renderWithProviders } from './test-utils';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
function sampleNodes(): NodeRecord[] {
|
||||
return [
|
||||
{ id: 1, name: 'parent', guid: 'p1', transitive: false, enable: true, status: 'online' },
|
||||
{ id: 0, name: 'child-a', guid: 'ca', parentGuid: 'p1', transitive: true },
|
||||
{ id: 0, name: 'child-b', guid: 'cb', parentGuid: 'p1', transitive: true },
|
||||
];
|
||||
}
|
||||
|
||||
describe('NodeList desktop table row keys', () => {
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
it('gives transitive sub-node rows distinct keys instead of colliding on id 0', () => {
|
||||
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
renderWithProviders(
|
||||
<NodeList
|
||||
nodes={sampleNodes()}
|
||||
isMobile={false}
|
||||
selectedIds={[]}
|
||||
onSelectionChange={noop}
|
||||
onAdd={noop}
|
||||
onMtls={noop}
|
||||
onEdit={noop}
|
||||
onDelete={noop}
|
||||
onProbe={noop}
|
||||
onToggleEnable={noop}
|
||||
onUpdateNode={noop}
|
||||
onUpdateSelected={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
const duplicateKeyWarning = errorSpy.mock.calls.some((call) =>
|
||||
call.some((arg) => typeof arg === 'string' && arg.includes('same key')),
|
||||
);
|
||||
expect(duplicateKeyWarning).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user