Skip to main content

useModalForm

useModalForm hook allows you to manage a form within a modal. It provides some useful methods to handle the form modal.

info

useModalForm hook is extended from useForm from the @pankod/refine-react-hook-form package.

Usage

We'll show two examples, one for creating and one for editing a post. Let's see how useModalForm is used in both.

Before we start, let's create a basic <Modal> component.

src/components/modal.tsx
type ModalPropsType = {
isOpen: boolean;
onClose: () => void;
};

export const Modal: React.FC<ModalPropsType> = ({
isOpen,
onClose,
children,
}) => {
if (!isOpen) return null;
return (
<>
<div className="overlay" onClick={onClose}></div>
<div className="modal">
<div className="modal-title">
<button className="close-button" onClick={onClose}>
&times;
</button>
</div>
<div className="modal-content">{children}</div>
</div>
</>
);
};
See styles
src/styles.css
* {
box-sizing: border-box;
}

.overlay {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
}

.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
z-index: 1000;
width: 500px;
overflow-y: auto;
}

.modal .modal-title {
display: flex;
justify-content: flex-end;
padding: 4px;
}

.modal .modal-content {
padding: 0px 16px 16px 16px;
}

.form {
display: flex;
flex-direction: column;
gap: 8px;
}

.form .form-group {
display: flex;
flex-direction: column;
gap: 4px;
}

.form input,
select,
textarea {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}

Create Modal

We'll create a PostList page that will display a list of posts. It will also display a <CreatePost> modal that will allow us to create a new post. We'll use useModalForm to manage the modal.

src/pages/posts/list.tsx
import { useTable } from "@pankod/refine-core";
import { useModalForm } from "@pankod/refine-react-hook-form";

import { CreatePost, EditPost } from "components";

export const PostList: React.FC = () => {
const { tableQueryResult } = useTable<IPost>({
initialSorter: [
{
field: "id",
order: "desc",
},
],
});

const createModalFormReturnValues = useModalForm({
refineCoreProps: { action: "create" },
});

const {
modal: { show: showCreateModal },
} = createModalFormReturnValues;

return (
<div>
<CreatePost {...createModalFormReturnValues} />
<button onClick={() => showCreateModal()}>Create Post</button>
<table>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{tableQueryResult.data?.data.map((post) => (
<tr key={post.id}>
<td>{post.id}</td>
<td>{post.title}</td>
<td>{post.status}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

interface IPost {
id: number;
title: string;
status: "published" | "draft" | "rejected";
}

<CreatePost> component will be used to create a new post.

src/components/post/create.tsx
import { UseModalFormReturnType } from "@pankod/refine-react-hook-form";

import { Modal } from "../modal";

export const CreatePost: React.FC<UseModalFormReturnType> = ({
register,
formState: { errors },
refineCore: { onFinish, formLoading },
handleSubmit,
modal: { visible, close },
saveButtonProps,
}) => {
return (
<Modal isOpen={visible} onClose={close}>
<form className="form" onSubmit={handleSubmit(onFinish)}>
<div className="form-group">
<label>Title: </label>
<input
{...register("title", {
required: "This field is required",
})}
/>
{errors.title && <span>{errors.title.message}</span>}
</div>
<div className="form-group">
<label>Status: </label>
<select {...register("status")}>
<option value="published">published</option>
<option value="draft">draft</option>
<option value="rejected">rejected</option>
</select>
</div>
<div className="form-group">
<label>Content: </label>
<textarea
{...register("content", {
required: "This field is required",
})}
rows={10}
/>
{errors.content && <span>{errors.content.message}</span>}
</div>
<button type="submit" {...saveButtonProps}>
{formLoading ? "Loading" : "Save"}
</button>
</form>
</Modal>
);
};
Create Modal

Edit Modal

Now we'll create a <EditPost> component that will allow us to edit a post within the modal. Also, we'll add a button to specify that wich post to edit.

src/pages/posts/list.tsx
import { useTable } from "@pankod/refine-core";
import { useModalForm } from "@pankod/refine-react-hook-form";

import { CreatePost, EditPost } from "components";
import { IPost } from "interfaces";

export const PostList: React.FC = () => {
const { tableQueryResult } = useTable<IPost>({
initialSorter: [
{
field: "id",
order: "desc",
},
],
});

const createModalFormReturnValues = useModalForm({
refineCoreProps: { action: "create" },
});
const editModalFormReturnValues = useModalForm({
refineCoreProps: { action: "edit" },
});

const {
modal: { show: showCreateModal },
} = createModalFormReturnValues;
const {
modal: { show: showEditModal },
} = editModalFormReturnValues;

return (
<div>
<CreatePost {...createModalFormReturnValues} />
<EditPost {...editModalFormReturnValues} />
<button onClick={() => showCreateModal()}>Create Post</button>
<table>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{tableQueryResult.data?.data.map((post) => (
<tr key={post.id}>
<td>{post.id}</td>
<td>{post.title}</td>
<td>{post.status}</td>
<td>
<button onClick={() => showEditModal(post.id)}>
Edit
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

<EditPost> component will be used to edit a post.

src/components/post/edit.tsx
import { UseModalFormReturnType } from "@pankod/refine-react-hook-form";

import { Modal } from "components/modal";

export const EditPost: React.FC<UseModalFormReturnType> = ({
register,
formState: { errors },
refineCore: { onFinish, formLoading },
handleSubmit,
modal: { visible, close },
saveButtonProps,
}) => {
return (
<Modal isOpen={visible} onClose={close}>
<form className="form" onSubmit={handleSubmit(onFinish)}>
<div className="form-group">
<label>Title: </label>
<input
{...register("title", {
required: "This field is required",
})}
/>
{errors.title && <span>{errors.title.message}</span>}
</div>
<div className="form-group">
<label>Status: </label>
<select {...register("status")}>
<option value="published">published</option>
<option value="draft">draft</option>
<option value="rejected">rejected</option>
</select>
</div>
<div className="form-group">
<label>Content: </label>
<textarea
{...register("content", {
required: "This field is required",
})}
rows={10}
/>
{errors.content && <span>{errors.content.message}</span>}
</div>
<button type="submit" {...saveButtonProps}>
{formLoading ? "Loading" : "Save"}
</button>
</form>
</Modal>
);
};
Edit Modal

API Reference

Properties

*: These properties have default values in RefineContext and can also be set on the <Refine> component.

External Props

It also accepts all props of useForm hook available in the React Hook Form.

Return values

PropertyDescriptionType
modalRelevant states and methods to control the modalModalReturnValues
refineCoreThe return values of the useForm in the coreUseFormReturnValues
React Hook Form Return ValuesSee React Hook Form documentation

  • ModalReturnValues

PropertyDescriptionType
visibleState of modal visibilityboolean
showSets the visible state to true(id?: BaseKey) => void
closeSets the visible state to false() => void
submitSubmits the form(values: TVariables) => void
titleModal title based on resource and action valuestring
saveButtonPropsProps for a submit button{ disabled: boolean, onClick: (e: React.BaseSyntheticEvent) => void; }

Live StackBlitz Example