ant-design / pro-components

🏆 Use Ant Design like a Pro!
https://pro-components.antdigital.dev
MIT License
4.23k stars 1.35k forks source link

🐛[BUG] EditableProTable 结合 proForm 使用,在表格头部插入数据时,导致其他数据丢失 #6572

Open ZhangJiL opened 1 year ago

ZhangJiL commented 1 year ago

提问前先看看:

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

🐛 bug 描述

EditableProTable 结合 proForm 使用,在表格头部插入数据时,导致其他数据丢失

📷 复现步骤

🏞 期望结果

💻 复现代码

参考 sandbox

© 版本信息

🚑 其他信息

423582ad2fd39a25492f4ef882c2d390

gogomusic commented 1 year ago

删除时也有此问题

chenshuai2144 commented 1 year ago

TAutomatically replying with ChatGPT can be attempted, but it cannot be guaranteed to be completely accurate and may not fully address all issues. Please feel free to reply if you have any further questions or concerns. 此回复基于 ChatGPT 自动生成,可以尝试下方案,官方员会在一定时间后继续继续处理。

从您提供的信息和截图来看,问题出现在通过 ProForm 的表单项来添加、修改和删除 EditableProTable 数据时,并且机制是通过 Redux 存储数据,同时在删、增、改某行数据的时候,其他行数据内容丢失。

可能原因:

EditableProTable 组件使用了 redux 作为数据存储机制,从数据流架构上分析,应该是在数据分发时,造成了数据的更新错误,导致某些行数据呈 “空” 状态。

解决方案:

  1. immutability-helper:Github地址:https://github.com/kolodny/immutability-helper;使用文档:https://github.com/kolodny/immutability-helper#readme

  2. Immer.js:Github地址:https://github.com/immerjs/immer;使用文档:https://immerjs.github.io/immer/docs/introduction

代码示例:您可以尝试使用 Immer.js 实现带有 Redux 的 EditableProTable。如下:

在你的 reducer 中:

import produce from "immer";

// 其他设置...

const reducer = produce((draftState, action) => {
  switch (action.type) {
    case "addRow":
       // 这里是逻辑代码
       // 此处使用 Immer.js
       const newData = { title: "测试标题", desc: "测试描述" };
       draftState.dataSource = [...draftState.dataSource, newData];
       break;

    case "updateRow":
       const { rowIndex, columnName, columnValue } = action.payload;
       // 这里也使用 Immer.js,设置新的 dataSource
       draftState.dataSource = draftState.dataSource.map((item, index) => {
         if (index === rowIndex) {
           return { ...item, [columnName]: columnValue };
         }
         return item;
       });
       break;

    case "deleteRow":
       const { rowIndex } = action.payload;
       // 删除一个row调用Immer.js
       draftState.dataSource = draftState.dataSource.slice(rowIndex);
       break;

    default:
      return draftState;
  }
});

export default reducer;

在您的 EditableProTable 组件中:

import { EditableProTable } from "@ant-design/pro-table";
import { produce } from "immer";

const Page: React.FC<PageProps> = () => {
  const [dataSource, setDataSource] = useState([]);

  const actionRef = useRef<ActionType>();

  const reducer = useReducer<Reducer<PageState, PageActions>>(pageReducer, {
    //初始化dataSource和其他状态
    dataSource: [],
    loading: false,
    count: 2
  });

  const handleRowDelete = (rowIndex: number) => {
    const [state, dispatch] = reducer;
    dispatch({ type: "deleteRow", payload: { rowIndex } });
  };

  const handleUpdateRow = (rowIndex: number, columnName: string, columnValue: string) => {
    const [state, dispatch] = reducer;
    dispatch({ type: "updateRow", payload: { rowIndex, columnName, columnValue } });
  };

  const handleRowAdd = () => {
    const [state, dispatch] = reducer;

    dispatch({ type: "addRow" });
  };

  //封装组件逻辑
  const handleTableRowChange = (
    list: PageState["dataSource"],
    record: PageState["dataSource"][0],
    _, //没有变化的列值
    action: string
  ) => {
    switch (action) {
      case "update":
        const { index, key, value } = record;
        console.log("update", { index, key, value });
        handleUpdateRow(index, key, value);
        break;
      case "delete":
        const { index } = record;
        console.log("delete", index);
        handleRowDelete(index);
        break;
      default:
        break;
    }

    setDataSource(list);
  };

  return (
    <Form>
      <ProFormList
        name="users"
        creatorButtonProps={{
          position: "top",
          creatorButtonText: "新增一行",
          max: 5,
          onCreator: handleRowAdd
        }}
        itemRender={(item, { list, action }, index) => {
          return (
            <Form.Item>
              <EditableProTable
                rowKey="index"
                columns={[
                  {
                    title: "标题",
                    dataIndex: "title",
                    //此处设置一个编辑框
                    renderFormItem: () => <Input />
                  },
                  {
                    title: "描述",
                    dataIndex: "desc",
                    // 此处必须设置为textarea,否则过长的字符串会出现无法编辑的错误
                    renderFormItem: () => <TextArea rows={1} />
                  }
                ]}
                value={list}
                onChange={(newList) => handleTableRowChange(newList, list, action)}
                onDelete={(a, record) => handleTableRowChange(a, record, action, "delete")}
                recordCreatorProps={false}
              />
            </Form.Item>
          );
        }}
      ></ProFormList>
    </Form>
  );
};

通过改用 Immer.js,你可以简化你的 reducer 操作,生成可读性更高的代码,同时,也可以优化内存的使用,减少可能存在的无谓的数据复制。

希望以上方案可以帮到您,若有不明白的地方或遇到新问题,请在评论中详细描述,并附上代码和相关参数,以便于问题精确定位和排查。

liushuhui commented 5 months ago

我也遇到了,只能通过这种方式吗

liushuhui commented 5 months ago

@chenshuai2144