This is the Modal with Form component which the fields value is not updating when open the modal with different mode and initialValues.
I tried to pass initialvalues into Antd Form, but somehow its not working as expected, the fields value only show after when i closed the 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>
);
};
This is the full code
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;
I tried to use useEffect to set the fields value, it kinda make it less buggy, but still doesn't solve the problem
useEffect(() => {
if (initialValues) {
form.setFieldsValue(initialValues);
}
}, [form, initialValues]);
This is removed
initialValues={initialValues}
initialValues
only works on first render. After that if you want to set values, use form.setFieldsValue
for multiple values or form.setFieldValue for single field. If you want to reset the form, use
form.resetField`.
I removed some of the code without changing the actual requirement.
I create form instance in Account
component instead of ModalForm
and pass form
as prop to ModalForm
component and connect it to the form. Also there's no need to use Form.Provider
. You can directly pass onFinish
function as prop to ModalForm
and connect it to the form. Also there's no need of useResetFormOnCloseModal
hook.
Here's the complete code
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;