为对话框选择新数据时,将错误地显示原始数据。只有在关闭对话框并重新打开之前尝试过的同一对话框后,表单才会显示正确的值。我相信存在一些
useEffect
和useState
恶作剧,但不知道如何解决。
要测试,请单击按钮
Data 1
验证数据是否正确,然后单击 Data 2
。它将显示与 Data1
而不是 Data 2
对应的数据,我们显然希望看到 Data 2
值。
在演示和实际实现中,需要 selectedId 状态,因为它是其他地方的参考。
import React, { useState, useCallback, useEffect } from "react";
import SomeDialog from "./SomeDialog";
import { FormData } from "./FormData";
import { Button } from "@mui/material";
let initialFormData: FormData[] = [
{ name: "data1", id: 0, location: "here" },
{ name: "data2", id: 1, location: "there" }
];
function Demo(): any {
const [selectedData, setSelectedData] = useState<FormData>(
initialFormData[0]
);
const [selectedId, setSelectedId] = useState<number>(0);
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
const handleDialogOpen = useCallback(() => setDialogOpen(true), []);
const handleDialogClose = useCallback(() => setDialogOpen(false), []);
const onClick = (index: number) => {
setSelectedId(index);
handleDialogOpen();
};
useEffect(() => {
let data = initialFormData.find((data) => data.id === selectedId);
setSelectedData(data);
}, [selectedId]);
return (
<div style={{ height: "100vh", width: "100vw" }}>
<Button
onClick={() => {
onClick(0);
}}
>
Data 1
</Button>
<Button
onClick={() => {
onClick(1);
}}
>
Data 2
</Button>
<SomeDialog
open={dialogOpen}
onClose={handleDialogClose}
data={selectedData}
/>
</div>
);
}
export default Demo;
对话框形式
import React, { useState, useEffect, ChangeEvent } from "react";
import TextField from "@mui/material/TextField";
import { Button, Dialog, Box } from "@mui/material";
import { FormData } from "./FormData";
interface SomeDialogProps {
open: boolean;
onClose: () => void;
data: FormData;
}
function SomeDialog(props: SomeDialogProps) {
const { open, onClose, data } = props;
const [formData, setFormData] = useState<FormData>(data);
useEffect(() => {
setFormData(data);
}, [data]);
function handleDataInputChange(event: ChangeEvent<HTMLInputElement>) {
const { id, value } = event.target;
setFormData({ ...formData, [id]: value });
}
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
console.info(formData);
onClose();
}
return (
<Dialog
open={open}
onClose={onClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box
component="form"
sx={{
"& .MuiTextField-root": { m: 1, width: "25ch" }
}}
noValidate
autoComplete="off"
onSubmit={handleSubmit}
>
<TextField
id="id"
label="Id"
defaultValue={formData.id}
onChange={handleDataInputChange}
/>
<TextField
id="name"
label="Name"
defaultValue={formData.name}
onChange={handleDataInputChange}
/>
<TextField
id="location"
label="Location"
defaultValue={formData.location}
onChange={handleDataInputChange}
/>
<Button type="submit">Submit</Button>
</Box>
</Dialog>
);
}
export default SomeDialog;
export interface FormData {
name: string;
id: number;
location: string;
}
我在这里看到两个问题:
在您的
Demo.tsx
文件中,setSelectedData(data)
中的 useEffect
调用是异步的。这意味着当您在设置handleDialogOpen()
后立即调用selectedId
时,selectedData
状态可能尚未更新,导致对话框显示旧数据。相反,您可以在调用 selectedId
之前将 selectedData
和 handleDialogOpen()
的逻辑组合在同一个函数中,如下所示:
const onClick = (index: number) => {
let data = initialFormData.find((data) => data.id === index);
setSelectedData(data);
setSelectedId(index);
handleDialogOpen();
};
这可确保当您打开对话框时,
selectedData
在打开对话框之前立即使用正确的数据进行更新。
类似地,在您的
SomeDialog.tsx
文件中,formData
设置为第一次渲染时 data
属性的值。然而,如上所述,React 状态更新是异步的,这意味着当 data
属性更改时,formData
状态变量不会在渲染周期中立即更新。
因此,如果
data
属性发生变化(例如,当您打开包含不同数据的对话框时),formData
仍将保留上一次渲染的值。这是因为更改 data
属性引起的状态更新还没有机会生效。
要解决此问题,您应该直接使用
data
属性来初始化表单字段,并且可以完全删除本地 formData
状态:
import React, { useEffect, ChangeEvent } from "react";
import TextField from "@mui/material/TextField";
import { Button, Dialog, Box } from "@mui/material";
import { FormData } from "./FormData";
interface SomeDialogProps {
open: boolean;
onClose: () => void;
data: FormData;
}
function SomeDialog(props: SomeDialogProps) {
const { open, onClose, data } = props;
function handleDataInputChange(event: ChangeEvent<HTMLInputElement>) {
const { id, value } = event.target;
// No need to update local state, directly use data prop
data[id] = value;
}
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
console.info(data);
onClose();
}
return (
<Dialog
open={open}
onClose={onClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box
component="form"
sx={{
"& .MuiTextField-root": { m: 1, width: "25ch" }
}}
noValidate
autoComplete="off"
onSubmit={handleSubmit}
>
<TextField
id="id"
label="Id"
defaultValue={data.id}
onChange={handleDataInputChange}
/>
<TextField
id="name"
label="Name"
defaultValue={data.name}
onChange={handleDataInputChange}
/>
<TextField
id="location"
label="Location"
defaultValue={data.location}
onChange={handleDataInputChange}
/>
<Button type="submit">Submit</Button>
</Box>
</Dialog>
);
}
export default SomeDialog;