Skip to main content
Version: 3.xx.xx

useForm

useForm is used to manage forms. It uses Ant Design Form data scope management under the hood and returns the required props for managing the form actions.

Usage

We'll show the basic usage of useForm by adding an editing form.

pages/posts/edit.tsx
import { Edit, Form, Input, useForm, Select } from "@pankod/refine-antd";

export const PostEdit: React.FC = () => {
const { formProps, saveButtonProps } = useForm<IPost>();

return (
<Edit saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item label="Title" name="title">
<Input />
</Form.Item>
<Form.Item label="Status" name="status">
<Select
options={[
{
label: "Published",
value: "published",
},
{
label: "Draft",
value: "draft",
},
{
label: "Rejected",
value: "rejected",
},
]}
/>
</Form.Item>
</Form>
</Edit>
);
};

interface IPost {
id: string;
title: string;
status: "published" | "draft" | "rejected";
}
const { formProps, saveButtonProps } = useForm<IPost>();

formProps includes all necessary values to manage Ant Design Form components.

In the example if you navigate to /posts/edit/1234 it will manage the data of the post with id of 1234 in an editing context. See Actions on how useForm determines this is an editing context.

Since this is an edit form it will fill the form with the data of the post with id of 1234 and then the form will be ready to edit further and submit the changes.

Submit functionality is provided by saveButtonProps which includes all of the necessary props for a button to submit a form including automatically updating loading states.

useForm accepts type parameters for the record in use and for the response type of the mutation. IPost in the example represents the record to edit. It is also used as the default type for mutation response.

Actions

useForm can handle edit, create and clone actions.

tip

By default it determines the action from route. In the example, the route is /posts/edit/1234 thus this is an editing form.

It can take an action parameter for the situations where it isn't possible to determine the action from route i.e. using a form in a modal, using a custom route.

const { formProps, saveButtonProps } = useForm({ action: "edit" });

action: "edit"

action: "edit" is used for editing an existing record. Form will initially be filled with the data of the record. By default, it uses the id from the route. It can be changed with the setId function or id property.

useForm uses useUpdate under the hood for mutations on edit mode.

action: "create"

action: "create"is used for creating a new record that didn't exist before.

useForm uses useCreate under the hood for mutations on create mode.

Clone mode

When creating a new record, useForm can initialize the form with the data of an existing record.

useForm works on clone mode when a route has a clone and id parameters like this {{resourceName}}/clone/1234. Alternatively, if route doesn't have those parameters, action can be set with action: "clone" and id can be set with setId and id.

const { setId, id } = useForm({
action: "clone",
});
tip

If you want to show a form in a modal or drawer where necessary route params might not be there you can use the useModalForm or the useDrawerForm hook.

tip

<CloneButton> can be used to navigate to a clone route with an id like this {{resourceName}}/clone/1234.

<CloneButton recordItemId={record.id} />

Also the clone method from the useNavigation hook can be used as well.

const { clone } = useNavigation();

<Button onClick={() => clone("posts", record.id)} />

How can I implement the save and continue feature?

refine provides you with the necessary methods to add this feature. This feature is familiar to Django users.

We have three save options: Save, Save and continue editing and Save and add another. By default, only the Save button is added from CRUD components for now.

Now let's see how to handle other cases,

pages/posts/create.tsx
import { Edit, Form, Input, useForm, Select, Space } from "@pankod/refine-antd";

export const PostEdit: React.FC = () => {
const {
formProps,
saveButtonProps,
onFinish,
redirect
} = useForm<IPost>({
redirect: false,
});

return (
<Edit
actionButtons={
<Space>
<SaveButton
{...saveButtonProps}
onClick={async () => {
await onFinish?.();
redirect("list");
}}
/>
<SaveButton {...saveButtonProps}>
Save and continue editing
</SaveButton>
<SaveButton {...saveButtonProps}
onClick={async () => {
await onFinish?.();
redirect("create");
}}>
Save and add another
</SaveButton>
</Space>
}
>
<Form {...formProps} layout="vertical">
<Form.Item label="Title" name="title">
<Input />
</Form.Item>
<Form.Item label="Status" name="status">
<Select
options={[
{
label: "Published",
value: "published",
},
{
label: "Draft",
value: "draft",
},
{
label: "Rejected",
value: "rejected",
},
]}
/>
</Form.Item>
</Form>
</Edit>
);
};

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

API Reference

Properties

PropertyDescriptionTypeDefault
actionType of the form mode"edit" | "create" | "clone"
resourceResource name for API data interactionsstring
idRecord id for fetchingBaseKeyId that it reads from the URL
onMutationSuccessCalled when a mutation is successful(data: UpdateResponse<M>, variables: any, context: any) => void
onMutationErrorCalled when a mutation encounters an error(error: any, variables: any, context: any) => void
mutationModeDetermines when mutations are executed "pessimistic | "optimistic | "undoable""pessimistic"*
submitOnEnterListens Enter key press to submit formbooleanfalse
warnWhenUnsavedChangesShows notification when unsaved changes existbooleanfalse*
redirectPage to redirect after a succesfull mutation "show" | "edit" | "list" | "create" | false"list"
undoableTimeoutDuration to wait before executing mutations when mutationMode = "undoable"number5000*
successNotificationSuccessful Mutation notificationSuccessErrorNotification"Successfully created resource" or "Successfully updated resource"
errorNotificationUnsuccessful Mutation notificationSuccessErrorNotification"There was an error creating resource (status code: statusCode)" or "Error when updating resource (status code: statusCode)"
metaDataMetadata query for dataProviderMetaDataQuery{}
liveModeWhether to update data automatically ("auto") or not ("manual") if a related live event is received. The "off" value is used to avoid creating a subscription."auto" | "manual" | "off""off"
liveParamsParams to pass to liveProvider's subscribe method if liveMode is enabled.{ ids?: BaseKey[]; [key: string]: any; }undefined
onLiveEventCallback to handle all related live events of this hook.(event: LiveEvent) => voidundefined
invalidatesYou can use it to manage the invalidations that will occur at the end of the mutation.all, resourceAll, list, many, detail, false["list", "many", "detail"]
queryOptionsreact-query queryOptions of useOne hook used while in edit mode. UseQueryOptions<
{ data: TData[]; },
TError>

*: These props have default values in RefineContext and can also be set on <Refine> component. useForm will use what is passed to <Refine> as default but a local value will override it.


Return values

PropertyDescriptionType
onFinishTriggers the mutation(values?: TVariables) => Promise<void>
formAnt Design form instanceFormInstance
formPropsAnt Design form propsFormProps
saveButtonPropsProps for a submit button{ disabled: boolean; onClick: () => void; loading?:boolean; }
redirectRedirect function for custom redirections(redirect: "list" | "edit" | "show" | "create" | false) => void
queryResultResult of the query of a recordQueryObserverResult<T>
mutationResultResult of the mutation triggered by submitting the formUseMutationResult<T>
formLoadingLoading state of form requestboolean
idRecord id for clone and create actionBaseKey
setIdid setterDispatch<SetStateAction< string | number | undefined>>

Type Parameters

PropertyDesriptionDefault
TDataResult data of the query that extends BaseRecordBaseRecord
TErrorCustom error object that extends HttpErrorHttpError
TVariablesValues for params.{}

Live Codesandbox Example