React Material UI 对话框更新时未正确设置表单数据状态

问题描述 投票:0回答:1

为对话框选择新数据时,将错误地显示原始数据。只有在关闭对话框并重新打开之前尝试过的同一对话框后,表单才会显示正确的值。我相信存在一些

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;
}

reactjs forms react-hooks material-ui dialog
1个回答
0
投票

我在这里看到两个问题:

演示.tsx

在您的

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

类似地,在您的

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;

© www.soinside.com 2019 - 2024. All rights reserved.