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

MUI确认对话框关闭后未重置输入状态的修复方案咨询

问题描述

基于Material-UI的确认对话框示例开发,当前示例包含三个文本输入框,实际应用会有更多输入项。遇到的问题是:修改输入框内容后点击取消按钮,重新打开对话框时,修改后的内容仍保留,未恢复原始值。

已知问题源于单独管理每个输入框的状态而非直接修改layout,但layout结构复杂且嵌套深,希望单独管理各输入状态,仅在保存时一次性更新layout,而非每次输入变更都修改layout,求修复方案。


示例代码

App.js

import ConfirmDialog from "./ConfirmDialog";

export default function App() {
  return (
    <div>
      <ConfirmDialog />
    </div>
  );
}

ConfirmDialog.js

import * as React from "react";
import PropTypes from "prop-types";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import DialogTitle from "@mui/material/DialogTitle";
import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import Dialog from "@mui/material/Dialog";
import TextField from "@mui/material/TextField";

function ConfirmationDialogRaw(props) {
  const { onClose, layout: layoutProp, open, ...other } = props;
  const [layout, setLayout] = React.useState(layoutProp);

  // 实际应用会有更多输入项
  const [title, setTitle] = React.useState(layout.title.text);
  const [firstName, setFirstName] = React.useState(layout.name.first);
  const [lastName, setLastName] = React.useState(layout.name.last);

  React.useEffect(() => {
    if (!open) {
      setLayout(layoutProp);
    }
  }, [layoutProp, open]);

  const handleCancel = () => {
    onClose();
  };

  const handleSave = () => {
    onClose({
      title: {
        text: title
      },
      name: {
        first: firstName,
        last: lastName
      }
    });
  };

  return (
    <Dialog
      sx={{ "& .MuiDialog-paper": { width: "80%", maxHeight: 435 } }}
      maxWidth="xs"
      open={open}
      {...other}
    >
      <DialogTitle>Layout</DialogTitle>
      <DialogContent dividers>
        <TextField
          margin="dense"
          id="title-label"
          label="Title"
          type="text"
          fullWidth
          variant="standard"
          value={title}
          onChange={(event) => setTitle(event.target.value)}
        />
        <TextField
          margin="dense"
          id="first-name-label"
          label="First Name"
          type="text"
          fullWidth
          variant="standard"
          value={firstName}
          onChange={(event) => setFirstName(event.target.value)}
        />
        <TextField
          margin="dense"
          id="last-name-label"
          label="Last Name"
          type="text"
          fullWidth
          variant="standard"
          value={lastName}
          onChange={(event) => setLastName(event.target.value)}
        />
      </DialogContent>
      <DialogActions>
        <Button autoFocus onClick={handleCancel}>
          Cancel
        </Button>
        <Button onClick={handleSave}>
          Save
        </Button>
      </DialogActions>
    </Dialog>
  );
}

ConfirmationDialogRaw.propTypes = {
  onClose: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
  value: PropTypes.string.isRequired
};

const defaultLayout = {
  title: {
    text: "Default title"
  },
  name: {
    first: "John",
    last: "Doe"
  }
};

export default function ConfirmationDialog() {
  const [open, setOpen] = React.useState(false);
  const [layout, setLayout] = React.useState(defaultLayout);

  const handleOpen = () => {
    setOpen(true);
  };

  const handleClose = (newLayout) => {
    setOpen(false);

    if (newLayout) {
      setLayout(newLayout);
    }
  };

  return (
    <Box>
      <Button variant="contained" onClick={handleOpen}>
        Open Dialog
      </Button>
      <ConfirmationDialogRaw
        id="layout-dialog"
        keepMounted
        open={open}
        onClose={handleClose}
        layout={layout}
      />
    </Box>
  );
}

修复方案

问题核心是对话框关闭/重新打开时,未同步重置单独管理的输入状态。当前代码只重置了layout状态,但title、firstName等输入状态没有跟着更新。以下是两种可行方案:

方案一:打开对话框时同步输入状态

修改ConfirmationDialogRaw组件,监听open和layoutProp的变化,每次对话框打开时,从最新的layoutProp中重新初始化输入状态:

function ConfirmationDialogRaw(props) {
  const { onClose, layout: layoutProp, open, ...other } = props;
  // 移除多余的layout状态,直接以layoutProp为数据源
  const [title, setTitle] = React.useState('');
  const [firstName, setFirstName] = React.useState('');
  const [lastName, setLastName] = React.useState('');

  React.useEffect(() => {
    // 对话框打开时,加载最新的原始值
    if (open) {
      setTitle(layoutProp.title.text);
      setFirstName(layoutProp.name.first);
      setLastName(layoutProp.name.last);
    }
  }, [layoutProp, open]);

  // handleCancel、handleSave及其余代码保持不变
}

方案二:点击取消时直接重置输入状态

在handleCancel中手动将所有输入状态重置为layoutProp的原始值,再关闭对话框:

const handleCancel = () => {
  // 重置为原始值
  setTitle(layoutProp.title.text);
  setFirstName(layoutProp.name.first);
  setLastName(layoutProp.name.last);
  onClose();
};

针对大量输入项的优化建议

如果输入项很多,逐个声明状态会非常繁琐,可以用一个对象统一管理所有输入状态,减少重复代码:

function ConfirmationDialogRaw(props) {
  const { onClose, layout: layoutProp, open, ...other } = props;
  const [formState, setFormState] = React.useState({
    title: layoutProp.title.text,
    firstName: layoutProp.name.first,
    lastName: layoutProp.name.last
  });

  React.useEffect(() => {
    if (open) {
      setFormState({
        title: layoutProp.title.text,
        firstName: layoutProp.name.first,
        lastName: layoutProp.name.last
      });
    }
  }, [layoutProp, open]);

  // 统一处理输入变更
  const handleInputChange = (field, value) => {
    setFormState(prev => ({ ...prev, [field]: value }));
  };

  const handleSave = () => {
    onClose({
      title: { text: formState.title },
      name: {
        first: formState.firstName,
        last: formState.lastName
      }
    });
  };

  // 输入框的onChange改为:
  // onChange={(e) => handleInputChange('title', e.target.value)}
  // 其余输入框同理
}

这样既保留了单独管理输入状态的需求,又能确保取消时恢复原始值,同时避免了大量重复的状态声明。


内容的提问来源于stack exchange,提问作者Simon1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 18:25:59