ant-design / ant-design/pro-components

👑 [需求]EditableProTable 受控模式下性能严重下降改善建议

Open
#8,674 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
4.8k
Forks
1.4k
Avg merge
10h 44m
Merged PRs (30d)
3

Description

提问前先看看:

https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md

### 🔩 所属模块或组件

EditableProTable

### 🥰 需求描述

EditableProTable受控模式一直存在性能问题,当同时编辑的元素太多,就会疯狂卡顿

见issue:https://github.com/ant-design/pro-components/issues/7974

并且官方没有推出的优化方案

### ⛰ 功能需求适用场景

改善EditableProTable在大量同时编辑的数据速度

### 🧐 解决方案

思路: 取消受控模式,并且利用useEffect和formRef进行state同步,并打开虚拟列表

举例:简单的受控模式

```
record.id}
recordCreatorProps={false}
onChange={setData}
toolBarRender={toolBar}
editable={{
type: 'multiple',
editableKeys: info.keys,
actionRender: (row, config, defaultDoms) => {
return [defaultDoms.delete];
},
onValuesChange: (_, list) => setData(list),
onChange: (editableKeys: React.Key[], editableRows: any) => {
console.log('change', editableKeys, editableRows);
},
}}
/>
```

首先要解决外部修改data的情况,

可以考虑使用useEffect + fast-deep-equal + formRef

这个原因是避免一次性更新全部,仅更新不同的(shouldUpdate,但是shouldUpdate无法应用与EditableProTable),导致巨量重渲染

```
useEffect(() => {
if (editorFormRef.current) {
data
.filter((v) => !equal(editorFormRef.current?.getRowData!(v.id), v))
.forEach((e) => {
editorFormRef.current?.setRowData!(e.id, e);
});
}
}, [data]);

```

EditablePro代码修改为:
```
record.id}
recordCreatorProps={false}
// 非受控模式下没用
//onChange={setDataSource}
toolBarRender={toolBar}
editable={{
type: 'multiple',
editableKeys: info.keys,
actionRender: (row, config, defaultDoms) => {
return [defaultDoms.delete];
},
onValuesChange: (_, list) => setData(list),,
onChange: (editableKeys: React.Key[], editableRows: any) => {
console.log('change', editableKeys, editableRows);
},
}}
/>

```

但是这样有个问题,用户在EditableProTable输入也会触发比对,这会造成bug,因此加上一个ref检测,排除掉用户输入情况

```
// 是否打开data同步,默认打开,但是当用户自己输入的时候关闭,避免产生不必要的重渲染
const triggerSync = useRef(true);
useEffect(() => {
if (editorFormRef.current && triggerSync.current) {
dataSource
.filter((v) => !equal(editorFormRef.current?.getRowData!(v.id), v))
.forEach((e) => {
editorFormRef.current?.setRowData!(e.id, e);
});
}
// 用户关闭,再次打开,ref不会触发重渲染,所以修改ref无所谓
triggerSync.current = true;
}, [dataSource]);

// 高频渲染函数,用useCallback提升下性能
const valueChange = useCallback((_: any, recordList: any[]) => {
triggerSync.current = false;
setData(recordList);
// 此时data正在等待队列,页面还未重渲染
// 我们应该在useEffect里面等待react重渲染后重新打开同步
// 而不是现在
}, []);

record.id}
recordCreatorProps={false}
// 非受控模式下没用
//onChange={setDataSource}
toolBarRender={toolBar}
editable={{
type: 'multiple',
editableKeys: info.keys,
actionRender: (row, config, defaultDoms) => {
return [defaultDoms.delete];
},
onValuesChange: valueChange ,
onChange: (editableKeys: React.Key[], editableRows: any) => {
console.log('change', editableKeys, editableRows);
},
}}
/>
```

在优化后,数千行同时编辑+实时保存变得很流畅,希望能给遇到这个问题一些解决方案

Contributor guide

Open the contributing guide

Research direction

Start at the EditableProTable controlled-mode entry point and inspect how value, onValuesChange, editableFormRef, and virtual are handled. Compare the proposed incremental synchronization with the controlled and uncontrolled examples; done should mean thousands of simultaneously edited rows remain responsive without synchronization being triggered by user input.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.