React Material UI Dialog表单数据更新时状态显示异常问题
问题描述
- 切换Dialog表单的数据源时,表单错误显示原始数据,必须关闭再重新打开才能展示正确值
- 测试步骤:点击
Data 1验证数据显示正常,点击Data 2后,表单仍展示Data 1对应内容,而非预期的Data 2数值 - 约束:
selectedId状态为必需,需在其他业务逻辑中引用
问题根源
- 状态更新异步冲突:点击按钮时先更新
selectedId再打开Dialog,但React状态更新是异步的,Dialog打开时selectedData尚未完成更新,导致传入Dialog的仍是旧数据 defaultValue特性限制:TextField的defaultValue仅在组件首次挂载时生效,后续props变化不会自动同步输入框内容,即使formData更新,输入框也不会刷新
解决方案
1. 修复父组件状态同步问题
直接通过索引获取目标数据,同步更新selectedData与selectedId,避免依赖异步的useEffect触发,确保打开Dialog时已拿到最新数据:
const onClick = (index: number) => { const targetData = initialFormData[index]; setSelectedId(index); setSelectedData(targetData); handleDialogOpen(); };
2. 修复Dialog输入框内容同步问题
将TextField的defaultValue改为value,实现受控组件,确保formData更新时输入框自动同步最新值:
// 修改SomeDialog中的TextField部分 <TextField id="id" label="Id" value={formData.id} onChange={handleDataInputChange} type="number" /> <TextField id="name" label="Name" value={formData.name} onChange={handleDataInputChange} /> <TextField id="location" label="Location" value={formData.location} onChange={handleDataInputChange} />
同时保留Dialog中的useEffect,确保外部data变化时formData能同步更新:
useEffect(() => { setFormData(data); }, [data]);
完整修改后代码
Demo.tsx
import React, { useState, useCallback } 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) => { const targetData = initialFormData[index]; setSelectedId(index); setSelectedData(targetData); handleDialogOpen(); }; 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;
SomeDialog.tsx
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]: id === "id" ? Number(value) : 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" value={formData.id} onChange={handleDataInputChange} type="number" /> <TextField id="name" label="Name" value={formData.name} onChange={handleDataInputChange} /> <TextField id="location" label="Location" value={formData.location} onChange={handleDataInputChange} /> <Button type="submit">Submit</Button> </Box> </Dialog> ); } export default SomeDialog;
内容的提问来源于stack exchange,提问作者Jack Hickson
相关产品推荐
相关产品推荐

