#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. Удивительно!, Добро пожаловать!, удачи 😀