материал-таблица после открытия диалогового окна в строке редактирования закрывается редактирование строки

#reactjs #material-ui #material-table

Вопрос:

Я использую в библиотеке Reactjs материал-таблицу. У меня есть кнопка для одного столбца для добавления изображения с помощью диалогового окна. Это хорошо работает для добавления новой строки, но если я хочу отредактировать строку после закрытия открытого диалога, отредактируйте строку. Я думаю, что эта проблема может быть вызвана повторным отправлением.

Открыть строку редактирования введите описание изображения здесь

После нажатия на кнопку открывается диалоговое окно для добавления изображения, но редактирование строки завершено, и я не могу сохранить изменения. введите описание изображения здесь

 import { Button, Container, IconButton } from "@material-ui/core";
import MaterialTable from "@material-table/core";
import React, { useState } from "react";
import { DropzoneDialogBase } from "material-ui-dropzone";
import CloseIcon from "@material-ui/icons/Close";
import { tableIcons } from "./IconProvider";

const App = () => {
  const [equipment, setEquipment] = useState([{url: '', name: 'Test'}]);
  const [open, setOpen] = useState(false);
  const [image, setImage] = useState([]);

  const FILES_LIMIT = 1;
  const pageSize = [10, 20, 50, 100];

  const dialogTitle = () => (
    <>
      <span>Upload file</span>
      <IconButton
        style={{ right: "12px", top: "8px", position: "absolute" }}
        onClick={() => setOpen(false)}
      >
        <CloseIcon />
      </IconButton>
    </>
  );

  return (
    <Container maxWidth={"xl"}>
      <MaterialTable
        columns={[
          {
            field: "url",
            title: "Url",
            editComponent: () => (
              <Button
                variant="contained"
                color="primary"
                onClick={() => setOpen(true)}
              >
                {"Add image"}
              </Button>
            ),
            render: (rowData) => <img src={rowData.url} />
          },
          { field: "name", title: "Name" }
        ]}
        data={equipment}
        title={"Title"}
        options={{
          pageSizeOptions: pageSize,
          pageSize: pageSize[0]
        }}
        icons={tableIcons}
        editable={{
          onRowAdd: (newData) =>
            new Promise((resolve, reject) => {
              setTimeout(() => {
                setEquipment([...equipment, newData]);

                resolve();
              }, 1000);
            }),
          onRowUpdate: (newData, oldData) =>
            new Promise((resolve, reject) => {
              setTimeout(() => {
                const dataUpdate = [...equipment];
                const index = oldData.tableData.id;
                dataUpdate[index] = newData;
                setEquipment([...dataUpdate]);

                resolve();
              }, 1000);
            }),
          onRowDelete: (oldData) =>
            new Promise((resolve, reject) => {
              setTimeout(() => {
                const dataDelete = [...equipment];
                const index = oldData.tableData.id;
                dataDelete.splice(index, 1);
                setEquipment([...dataDelete]);

                resolve();
              }, 1000);
            })
        }}
      />

      <DropzoneDialogBase
        dialogTitle={dialogTitle()}
        acceptedFiles={["image/*"]}
        fileObjects={image}
        cancelButtonText={"cancel"}
        submitButtonText={"submit"}
        maxFileSize={5000000}
        filesLimit={FILES_LIMIT}
        open={open}
        onAdd={(newFileObjs) => {
          setImage([].concat([], newFileObjs));
        }}
        onDelete={(deleteFileObj) => {
          setImage(image.filter((item) => item !== deleteFileObj));
        }}
        onClose={() => setOpen(false)}
        onSave={() => {
          setOpen(false);
        }}
        showPreviews={true}
        showFileNamesInPreview={true}
      />
    </Container>
  );
};

export default App;
 

Я создаю пример в Codesandbox

Ответ №1:

Вы правы, проблема возникает, так как вы запускаете повторный запуск при каждом обновлении состояния, это происходит, когда столбцы, предоставленные в таблицу материалов, статичны, но содержат функции, которые обновляются при каждом рендеринге, сбрасывая состояние таблицы. Предоставьте стабильную функцию или ссылку на столбец или идентификатор строки, чтобы предотвратить потерю состояния.

Итак, вы исправляете это:

   const preventRerender = useCallback((rowData) => <img src={rowData.url} />, []);
  const editComponent = useCallback(() => {
    return <Button
      variant="contained"
      color="primary"
      onClick={() => setOpen(true)}
    >
      {"Add image"}
    </Button>;
  }, [])

<MaterialTable
        columns={[
          {
            field: "url",
            title: "Url",
            editComponent,
            render: preventRerender
          },
          { field: "name", title: "Name" }
        ]}
 

добавив обратный вызов или useMemo, вы предотвратите состояние обновления, если хотите, с помощью зависимости от массива обновлений.

Дополнительное примечание, следите и заботьтесь о ключе/идентификаторе строк

Демо-версия песочницы

Комментарии:

1. Это работает так, как и должно быть. Большое спасибо. Вы сэкономили мне много времени и стресса 🙂

2. Удивительно!, Добро пожаловать!, удачи 😀