打开不同模式和初始值的模态时如何更新字段值?

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

这是带有表单组件的模态,当打开具有不同模式和初始值的模态时,字段值不会更新。

我尝试将初始值传递到 Antd 表单中,但不知何故它没有按预期工作,字段值仅在我关闭 Modal 后显示。

const useResetFormOnCloseModal = ({ form, open }: { form: FormInstance; open: boolean }) => {
  const prevOpenRef = useRef<boolean>();
  useEffect(() => {
    prevOpenRef.current = open;
  }, [open]);
  const prevOpen = prevOpenRef.current;

  useEffect(() => {
    if (!open && prevOpen) {
      form.resetFields();
    }
  }, [form, prevOpen, open]);
};

interface ModalFormProps {
  open: boolean;
  onCancel: () => void;
  initialValues?: UserData;
  mode: "add" | "edit";
}

const ModalForm = ({ open, onCancel, initialValues, mode }: ModalFormProps) => {
  const [form] = Form.useForm();

  useResetFormOnCloseModal({
    form,
    open,
  });

  const onOk = () => {
    form.submit();
  };

  return (
    <Modal
      forceRender
      title={`${mode == "add" ? "Add" : "Edit"} Account`}
      open={open}
      onOk={onOk}
      onCancel={onCancel}
    >
      <Form form={form} layout="vertical" initialValues={initialValues}>
        <Form.Item
          label="Name"
          name="name"
          rules={[{ required: true, message: "Please input the name!" }]}
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Student ID"
          name="student_id"
          rules={[{ required: true, message: "Please input the student ID!" }]}
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Password"
          name="password"
          rules={[{ required: true, message: "Please input the password!" }]}
        >
          <Input.Password />
        </Form.Item>
      </Form>
    </Modal>
  );
};

这是完整代码

import { useEffect, useRef, useState } from "react";
import { Table, Space, Popconfirm, Button, Modal, Form, Input, FormInstance } from "antd";
import { EyeOutlined, EyeInvisibleOutlined, PlusOutlined } from "@ant-design/icons";
import { addAccount, deleteAccount, getAccounts } from "../../api/accounts";
import useAccountState, { UserData } from "../../hooks/states/useAccountState";
import useApp from "../../hooks/useApp";
import { useAuth } from "../../hooks/useAuth";

const useResetFormOnCloseModal = ({ form, open }: { form: FormInstance; open: boolean }) => {
  const prevOpenRef = useRef<boolean>();
  useEffect(() => {
    prevOpenRef.current = open;
  }, [open]);
  const prevOpen = prevOpenRef.current;

  useEffect(() => {
    if (!open && prevOpen) {
      form.resetFields();
    }
  }, [form, prevOpen, open]);
};

interface ModalFormProps {
  open: boolean;
  onCancel: () => void;
  initialValues?: UserData;
  mode: "add" | "edit";
}

const ModalForm = ({ open, onCancel, initialValues, mode }: ModalFormProps) => {
  const [form] = Form.useForm();

  useResetFormOnCloseModal({
    form,
    open,
  });

  const onOk = () => {
    form.submit();
  };

  return (
    <Modal
      forceRender
      title={`${mode == "add" ? "Add" : "Edit"} Account`}
      open={open}
      onOk={onOk}
      onCancel={onCancel}
    >
      <Form form={form} layout="vertical" initialValues={initialValues}>
        <Form.Item
          label="Name"
          name="name"
          rules={[{ required: true, message: "Please input the name!" }]}
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Student ID"
          name="student_id"
          rules={[{ required: true, message: "Please input the student ID!" }]}
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Password"
          name="password"
          rules={[{ required: true, message: "Please input the password!" }]}
        >
          <Input.Password />
        </Form.Item>
      </Form>
    </Modal>
  );
};

const Account = () => {
  const [showPasswordsFor, setShowPasswordsFor] = useState<string[]>([]);
  const [modalOpen, setModalOpen] = useState(false);
  const [modalMode, setModalMode] = useState<"add" | "edit">("add");
  const [modalFormValues, setModalFormValues] = useState<UserData | undefined>();

  const { accounts, setAccounts, loading, setLoading } = useAccountState();
  const { notification } = useApp();
  const { user } = useAuth();

  useEffect(() => {
    refresh();
  }, []);

  const refresh = async () => {
    const { data, error } = await getAccounts();
    if (!error) {
      setAccounts(data);
      setLoading(false);
    }
  };

  const togglePasswordVisibility = (key: string) => {
    setShowPasswordsFor((prevState) =>
      prevState.includes(key) ? prevState.filter((k) => k !== key) : [...prevState, key]
    );
  };

  const handleAdd = () => {
    setModalOpen(true);
    setModalFormValues(undefined);
    setModalMode("add");
  };

  const handleEdit = (record: UserData) => {
    setModalOpen(true);
    setModalFormValues(record);
    setModalMode("edit");
  };

  const handleDelete = async (record: UserData) => {
    setLoading(true);
    const { error } = await deleteAccount(record._id);
    if (error) {
      notification.error({
        message: "Failed To Delete",
        description: "Unexpected error occurred, please try again.",
      });
    } else {
      notification.success({
        message: "Deleted Successfully",
        description: `You've have deleted account \"${record.name}\"`,
      });
    }
    refresh();
    setLoading(false);
  };

  const handleCancel = () => {
    setModalOpen(false);
  };

  const onFinish = async (_: string, { values }: { values: any }) => {
    setLoading(true);
    const payload = {
      ...values,
      user_id: user!.id,
    };
    const { error } = await addAccount(payload);
    if (error) {
      notification.error({
        message: "Failed To Add Account",
        description: "Unexpected error occurred, please try again.",
      });
    } else {
      notification.success({
        message: "Added Successfully",
        description: `You've have added account \"${values.name}\"`,
      });
    }
    setModalOpen(false);
    refresh();
    setLoading(false);
  };

  const columns = [
    {
      width: "8%",
      title: "#",
      key: "index",
      render: (_: string, __: UserData, index: number) => index + 1,
    },
    {
      width: "20%",
      title: "Name",
      dataIndex: "name",
      key: "name",
    },
    {
      width: "30%",
      title: "Student ID",
      dataIndex: "student_id",
      key: "student_id",
    },
    {
      width: "30%",
      title: "Password",
      dataIndex: "password",
      key: "password",
      render: (text: string, record: UserData) => (
        <Space>
          {showPasswordsFor.includes(record._id) ? text : "*".repeat(12)}
          {showPasswordsFor.includes(record._id) ? (
            <EyeInvisibleOutlined onClick={() => togglePasswordVisibility(record._id)} />
          ) : (
            <EyeOutlined onClick={() => togglePasswordVisibility(record._id)} />
          )}
        </Space>
      ),
    },
    {
      width: "12%",
      title: "Action",
      key: "action",
      render: (_: string, record: UserData) => (
        <Space size="middle">
          <a onClick={() => handleEdit(record)}>Edit</a>
          <Popconfirm title="Sure to delete?" onConfirm={() => handleDelete(record)}>
            <a>Delete</a>
          </Popconfirm>
        </Space>
      ),
    },
  ];

  return (
    <Space direction="vertical" size={16}>
      <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
        Add Account
      </Button>
      <Table
        loading={loading}
        scroll={{ x: 540 }}
        size="small"
        columns={columns}
        dataSource={accounts}
        rowKey="_id"
        bordered
      />
      <Form.Provider onFormFinish={onFinish}>
        <ModalForm
          mode={modalMode}
          open={modalOpen}
          onCancel={handleCancel}
          initialValues={modalFormValues}
        />
      </Form.Provider>
    </Space>
  );
};

export default Account;

我尝试使用useEffect来设置字段值,这有点减少bug,但仍然没有解决问题

 useEffect(() => {
    if (initialValues) {
      form.setFieldsValue(initialValues);
    }
  }, [form, initialValues]);

已删除

initialValues={initialValues}
reactjs forms antd
1个回答
0
投票

initialValues
仅适用于第一次渲染。之后,如果您想设置值,请使用
form.setFieldsValue
来设置多个值或
form.setFieldValue for single field. If you want to reset the form, use 
form.resetField`。

我删除了一些代码,但没有改变实际需求。 我在

Account
组件而不是
ModalForm
中创建表单实例,并将
form
作为属性传递给
ModalForm
组件,并将其连接到表单。也没有必要使用
Form.Provider
。您可以直接将
onFinish
函数作为 prop 传递给
ModalForm
并将其连接到表单。而且也不需要
useResetFormOnCloseModal
挂钩。

这是完整的代码

import { EyeInvisibleOutlined, EyeOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, type FormInstance, Input, Modal, Space, Table } from 'antd';
import { useState } from 'react';

interface UserData {
    _id: string;
    name: string;
    student_id: string;
    password: string;
}

interface ModalFormProps {
    open: boolean;
    onCancel: () => void;
    mode: 'add' | 'edit';
    form: FormInstance;
    onFinish: (values: UserData) => void;
}

const ModalForm = ({ open, onCancel, mode, form, onFinish }: ModalFormProps) => {
    return (
        <Modal title={`${mode === 'add' ? 'Add' : 'Edit'} Account`} open={open} onOk={form.submit} onCancel={onCancel}>
            <Form form={form} layout='vertical' onFinish={onFinish}>
                <Form.Item label='Name' name='name' rules={[{ required: true, message: 'Please input the name!' }]}>
                    <Input />
                </Form.Item>
                <Form.Item label='Student ID' name='student_id' rules={[{ required: true, message: 'Please input the student ID!' }]}>
                    <Input />
                </Form.Item>
                <Form.Item label='Password' name='password' rules={[{ required: true, message: 'Please input the password!' }]}>
                    <Input.Password />
                </Form.Item>
            </Form>
        </Modal>
    );
};

const Account = () => {
    const [showPasswordsFor, setShowPasswordsFor] = useState<Array<string>>([]);
    const [modalOpen, setModalOpen] = useState(false);
    const [modalMode, setModalMode] = useState<'add' | 'edit'>('add');
    const [form] = Form.useForm<UserData>();

    const togglePasswordVisibility = (key: string) => {
        setShowPasswordsFor((prevState) => (prevState.includes(key) ? prevState.filter((k) => k !== key) : [...prevState, key]));
    };

    const handleAdd = () => {
        setModalOpen(true);
        form.resetFields();
        setModalMode('add');
    };

    const handleEdit = (record: UserData) => {
        setModalOpen(true);
        form.setFieldsValue(record);
        setModalMode('edit');
    };

    const handleCancel = () => {
        setModalOpen(false);
    };

    const onFinish = (values: UserData) => {
        console.log('Received values of form: ', values);

        setModalOpen(false);
    };

    const columns = [
        { width: '8%', title: '#', key: 'index', render: (_: string, __: UserData, index: number) => index + 1 },
        { width: '20%', title: 'Name', dataIndex: 'name', key: 'name' },
        { width: '30%', title: 'Student ID', dataIndex: 'student_id', key: 'student_id' },
        {
            width: '30%',
            title: 'Password',
            dataIndex: 'password',
            key: 'password',
            render: (text: string, record: UserData) => (
                <Space>
                    {showPasswordsFor.includes(record._id) ? text : '*'.repeat(12)}
                    {showPasswordsFor.includes(record._id) ? (
                        <EyeInvisibleOutlined onClick={() => togglePasswordVisibility(record._id)} />
                    ) : (
                        <EyeOutlined onClick={() => togglePasswordVisibility(record._id)} />
                    )}
                </Space>
            )
        },
        {
            width: '12%',
            title: 'Action',
            key: 'action',
            render: (_: string, record: UserData) => (
                <Space size='middle'>
                    <a onClick={() => handleEdit(record)}>Edit</a>
                </Space>
            )
        }
    ];

    return (
        <Space direction='vertical' size={16}>
            <Button type='primary' icon={<PlusOutlined />} onClick={handleAdd}>
                Add Account
            </Button>
            <Table
                scroll={{ x: 540 }}
                size='small'
                columns={columns}
                dataSource={[
                    { _id: '1', name: 'John Doe', student_id: '2018-0001', password: 'password' },
                    { _id: '2', name: 'Jane Doe', student_id: '2018-0002', password: 'password' },
                    { _id: '3', name: 'John Doe', student_id: '2018-0003', password: 'password' }
                ]}
                rowKey='_id'
                bordered
            />
            <ModalForm mode={modalMode} open={modalOpen} onCancel={handleCancel} form={form} onFinish={onFinish} />
        </Space>
    );
};

export default Account;
© www.soinside.com 2019 - 2024. All rights reserved.