You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Material UI Dialog表单数据更新时状态显示异常问题

问题描述
  • 切换Dialog表单的数据源时,表单错误显示原始数据,必须关闭再重新打开才能展示正确值
  • 测试步骤:点击Data 1验证数据显示正常,点击Data 2后,表单仍展示Data 1对应内容,而非预期的Data 2数值
  • 约束:selectedId状态为必需,需在其他业务逻辑中引用
问题根源
  1. 状态更新异步冲突:点击按钮时先更新selectedId再打开Dialog,但React状态更新是异步的,Dialog打开时selectedData尚未完成更新,导致传入Dialog的仍是旧数据
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:43:19