这是带有表单组件的模态,当打开具有不同模式和初始值的模态时,字段值不会更新。
我尝试将初始值传递到 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}
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;