Skip to content

Commit e691b1a

Browse files
authored
fix: Do not blink when columns change with fixed (#403)
* fix: Update key calucation logic * add column count as useMemo of sticky offset deps
1 parent 7162664 commit e691b1a

File tree

5 files changed

+39
-154
lines changed

5 files changed

+39
-154
lines changed

examples/debug.tsx

Lines changed: 16 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,34 @@
11
/* eslint-disable no-console,func-names,react/no-multi-comp */
22
import React from 'react';
3-
import Table, { Column, ColumnGroup } from '../src';
3+
import Table from '../src';
44
import '../assets/index.less';
55
import { ColumnsType } from '../src/interface';
66

7-
interface RecordType {
8-
key: React.Key;
9-
id: number;
10-
date: number;
11-
}
12-
13-
let UUID = 0;
14-
15-
function renderDate(timestamp: number, record: RecordType) {
16-
return (
17-
<span style={{ color: 'pink' }}>
18-
{record.id
19-
.toString(36)
20-
.substr(4)
21-
.toUpperCase()}{' '}
22-
- {new Date(timestamp).toString()}
23-
</span>
24-
);
25-
}
7+
const columns: ColumnsType = [
8+
{ dataIndex: 'a', title: 'a' },
9+
{ dataIndex: 'b', title: 'b' },
10+
{ dataIndex: 'c', title: 'c', fixed: 'right', width: 200 },
11+
];
2612

2713
const Demo = () => {
28-
const [data, setData] = React.useState([]);
29-
30-
function offsetIndex(record: RecordType, offset: number) {
31-
const index = data.indexOf(record);
32-
const targetIndex = index + offset;
33-
const target = data[targetIndex];
34-
const newData = [...data];
35-
newData[index] = target;
36-
newData[targetIndex] = record;
37-
38-
setData(newData);
39-
}
40-
41-
const columns: ColumnsType<RecordType> = [
42-
{ title: 'ID', key: 'id', dataIndex: 'id', width: 100 },
43-
{
44-
title: 'Date',
45-
dataIndex: 'date',
46-
width: 200,
47-
render: renderDate,
48-
},
49-
{
50-
title: 'Merged Title',
51-
children: [
52-
{
53-
title: '0 - ID',
54-
dataIndex: 'id',
55-
},
56-
{
57-
title: '1 - Merge Date',
58-
children: [
59-
{
60-
title: '1 - 0 - ID',
61-
dataIndex: 'id',
62-
},
63-
{
64-
title: '1 - 1 - Date',
65-
dataIndex: 'date',
66-
},
67-
],
68-
},
69-
],
70-
},
71-
{
72-
title: 'Operations',
73-
render(_: null, record: RecordType, index: number) {
74-
return (
75-
<div>
76-
<button
77-
type="button"
78-
onClick={() => {
79-
offsetIndex(record, 1);
80-
}}
81-
disabled={index === data.length - 1}
82-
>
83-
84-
</button>
85-
<button
86-
type="button"
87-
onClick={() => {
88-
offsetIndex(record, -1);
89-
}}
90-
disabled={index === 0}
91-
>
92-
93-
</button>
94-
</div>
95-
);
96-
},
97-
},
98-
];
99-
100-
const addData = () => {
101-
setData(originData => {
102-
UUID += 1000 + Math.floor(Math.random() * 100000);
103-
104-
const id = Date.now() + UUID;
105-
106-
return [
107-
...originData,
108-
{
109-
key: id,
110-
id,
111-
date: id,
112-
},
113-
];
114-
});
115-
};
116-
117-
React.useEffect(() => {
118-
for (let i = 0; i < 3; i += 1) {
119-
addData();
120-
}
121-
}, []);
122-
123-
const [, forceUpdate] = React.useState();
14+
const [showB, setShowB] = React.useState(true);
15+
const myColumns = showB ? columns : columns.filter(({ title }) => title !== 'b');
12416

12517
return (
12618
<div>
127-
<h2>Debug Usage, remove after stable released</h2>
128-
<button type="button" onClick={addData}>
129-
Add Row
130-
</button>
131-
<Table<RecordType> columns={columns} data={data} />
132-
133-
<br />
134-
135-
<Table<RecordType> data={data}>
136-
<Column dataIndex="id" title="ID" />
137-
<ColumnGroup title="Merged Title">
138-
<Column dataIndex="id" title="0 - ID" />
139-
<Column dataIndex="date" title="1 - Date" />
140-
</ColumnGroup>
141-
</Table>
142-
143-
<br />
144-
145-
<Table<RecordType> data={data}>
146-
<Column dataIndex="id" title="ID" />
147-
<Column dataIndex="id" title="Merged Title" colSpan={2} />
148-
<Column dataIndex="date" colSpan={0} />
149-
</Table>
150-
151-
<Table<RecordType> />
152-
19+
<Table<any>
20+
scroll={{ x: 2000 }}
21+
tableLayout="auto"
22+
columns={myColumns}
23+
data={[{ a: 1, b: 2, c: 3, key: 1 }]}
24+
/>
15325
<button
15426
type="button"
15527
onClick={() => {
156-
forceUpdate(Math.random());
28+
setShowB(!showB);
15729
}}
15830
>
159-
Update
31+
Trigger {showB.toString()}
16032
</button>
16133
</div>
16234
);

src/Body/BodyRow.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import ResizeObserver from 'rc-resize-observer';
44
import Cell from '../Cell';
55
import TableContext from '../context/TableContext';
66
import BodyContext from '../context/BodyContext';
7-
import { getColumnKey } from '../utils/valueUtil';
7+
import { getColumnsKey } from '../utils/valueUtil';
88
import {
99
ColumnType,
1010
StickyOffsets,
@@ -114,6 +114,7 @@ function BodyRow<RecordType extends { children?: RecordType[] }>(props: BodyRowP
114114
computeRowClassName = rowClassName(record, index, indent);
115115
}
116116

117+
const columnsKey = getColumnsKey(flattenColumns);
117118
const baseRowNode = (
118119
<RowComponent
119120
{...additionalProps}
@@ -133,7 +134,7 @@ function BodyRow<RecordType extends { children?: RecordType[] }>(props: BodyRowP
133134
{flattenColumns.map((column: ColumnType<RecordType>, colIndex) => {
134135
const { render, dataIndex, className: columnClassName } = column;
135136

136-
const key = getColumnKey(column, colIndex);
137+
const key = columnsKey[colIndex];
137138
const fixedInfo = fixedInfoList[colIndex];
138139

139140
// ============= Used for nest expandable =============

src/Header/HeaderRow.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import TableContext from '../context/TableContext';
1212
import { getCellFixedInfo } from '../utils/fixUtil';
1313
import ResizeContext from '../context/ResizeContext';
14+
import { getColumnsKey } from '../utils/valueUtil';
1415

1516
export interface RowProps<RecordType> {
1617
cells: CellType<RecordType>[];
@@ -39,6 +40,8 @@ function HeaderRow<RecordType>({
3940
rowProps = onHeaderRow(cells.map(cell => cell.column), index);
4041
}
4142

43+
const columnsKey = getColumnsKey(cells.map(cell => cell.column));
44+
4245
return (
4346
<RowComponent {...rowProps}>
4447
{cells.map((cell: CellType<RecordType>, cellIndex) => {
@@ -62,7 +65,7 @@ function HeaderRow<RecordType>({
6265
align={column.align}
6366
component={CellComponent}
6467
prefixCls={prefixCls}
65-
key={cellIndex}
68+
key={columnsKey[cellIndex]}
6669
{...fixedInfo}
6770
additionalProps={additionalProps}
6871
/>

src/hooks/useStickyOffsets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function useStickyOffsets(colWidths: number[], columCount: number) {
2626
left: leftOffsets,
2727
right: rightOffsets,
2828
};
29-
}, [colWidths]);
29+
}, [colWidths, columCount]);
3030

3131
return stickyOffsets;
3232
}

src/utils/valueUtil.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,23 @@ interface GetColumnKeyColumn {
4040
dataIndex?: DataIndex;
4141
}
4242

43-
export function getColumnKey({ key, dataIndex }: GetColumnKeyColumn, index: number) {
44-
if (key) {
45-
return key;
46-
}
43+
export function getColumnsKey(columns: GetColumnKeyColumn[]) {
44+
const columnKeys: React.Key[] = [];
45+
const keys: Record<React.Key, boolean> = {};
46+
47+
columns.forEach(column => {
48+
const { key, dataIndex } = column || {};
4749

48-
const prefix = toArray(dataIndex).join('-') || INTERNAL_KEY_PREFIX;
50+
let mergedKey = key || toArray(dataIndex).join('-') || INTERNAL_KEY_PREFIX;
51+
while (keys[mergedKey]) {
52+
mergedKey = `${mergedKey}_next`;
53+
}
54+
keys[mergedKey] = true;
55+
56+
columnKeys.push(mergedKey);
57+
});
4958

50-
return `${prefix}_${index}`;
59+
return columnKeys;
5160
}
5261

5362
export function mergeObject<ReturnObject extends object>(

0 commit comments

Comments
 (0)