useForm
A hook that orchestrates Refine's data hooks to create, edit, and clone data. It also provides a set of features to make it easier for users to implement their real world needs and handle edge cases such as redirects, invalidation, auto-save and more.
import { useForm } from "@refinedev/core";
const { onFinish, ... } = useForm({ ... });
@refinedev/antd, @refinedev/mantine and @refinedev/react-hook-form packages provide their own extended versions of useForm hook with full support for their respective form implementations including validation, error handling, form values, and more.
Refer to their respective documentation for more information and check out the Forms in Refine guide for detailed information on how to handle forms in Refine.
Usage
Basic usage of the useForm hook demonstrates how to use the hook in all three modes, create, edit, and clone.
Code Example
// file: /create.tsx
import React from "react";
import { useForm, HttpError, BaseKey } from "@refinedev/core";
export const Create: React.FC = () => {
const { onFinish } = useForm<IProduct, HttpError, FormValues>({
resource: "products",
action: "create",
redirect: "show", // redirect to show page after form submission, defaults to "list"
});
const [values, setValues] = React.useState<FormValues>({ name: "", material: "" });
const onSubmit = (e) => {
e.preventDefault();
onFinish(values);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input
name="name"
placeholder="Name"
value={values.name}
onChange={(e) => setValues({ ...values, name: e.target.value })}
/>
<label htmlFor="material">Material</label>
<input
name="material"
placeholder="Material"
value={values.material}
onChange={(e) => setValues({ ...values, material: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
);
};
interface IProduct {
id: BaseKey;
name: string;
material: string;
}
interface FormValues {
name?: string;
material?: string;
}// file: /edit.tsx
import React from "react";
import { useForm, HttpError, BaseKey } from "@refinedev/core";
export const Edit: React.FC = () => {
const { query, formLoading, onFinish } = useForm<IProduct, HttpError, FormValues>({
resource: "products",
action: "edit",
id: "123",
redirect: "show", // redirect to show page after form submission, defaults to "list"
});
const defaultValues = query?.data?.data;
const [values, setValues] = React.useState<FormValues>({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});
React.useEffect(() => {
setValues({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});
}, [defaultValues]);
const onSubmit = (e) => {
e.preventDefault();
onFinish(values);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input
name="name"
placeholder="Name"
value={values.name}
onChange={(e) => setValues({ ...values, name: e.target.value })}
/>
<label htmlFor="material">Material</label>
<input
name="material"
placeholder="Material"
value={values.material}
onChange={(e) => setValues({ ...values, material: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
);
};
interface IProduct {
id: BaseKey;
name: string;
material: string;
}
interface FormValues {
name?: string;
material?: string;
}// file: /clone.tsx
import React from "react";
import { useForm, HttpError, BaseKey } from "@refinedev/core";
export const Clone: React.FC = () => {
const { query, formLoading, onFinish } = useForm<IProduct, HttpError, FormValues>({
resource: "products",
action: "clone",
id: "123",
redirect: "show", // redirect to show page after form submission, defaults to "list"
});
const defaultValues = query?.data?.data;
const [values, setValues] = React.useState<FormValues>({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});
React.useEffect(() => {
setValues({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});
}, [defaultValues]);
const onSubmit = (e) => {
e.preventDefault();
onFinish(values);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input
name="name"
placeholder="Name"
value={values.name}
onChange={(e) => setValues({ ...values, name: e.target.value })}
/>
<label htmlFor="material">Material</label>
<input
name="material"
placeholder="Material"
value={values.material}
onChange={(e) => setValues({ ...values, material: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
);
};
interface IProduct {
id: BaseKey;
name: string;
material: string;
}
interface FormValues {
name?: string;
material?: string;
}Parameters
action Router IntegratedThis value can be inferred from the route. Click to see the guide for more information.
The action that will be performed with the submission of the form. Can be create, edit, or clone. If not specified, it will be determined by the current route or fallback to create.
useForm({ action: "create" });
Create
In create action, useForm will follow the flow below:
After form is submitted:
useFormcallsonFinishfunction with the form values.onFinishfunction callsuseCreatewith the form values.useCreatecallsdataProvider'screatefunction and returns the response.useFormcallsonSuccessoronErrorfunction with the response, depending on the response status.- After a successful mutation,
useFormwill invalidate the queries specified ininvalidatesprop. onSuccessoronErrorfunction then calls theopenfunction of thenotificationProviderto inform the user.useFormredirects to thelistpage.
Edit
In edit action, useForm will follow the flow below:
When useForm is mounted, it calls useOne hook to retrieve the record to be edited. The id for the record is obtained from the props or the current route.
After form is submitted:
useFormcallsonFinishfunction with the form values.onFinishfunction callsuseUpdatewith the form values.- If the mutation mode is
optimisticorundoable,useFormwill update the query cache with the form values immediately after the mutation is triggered. - If the mutation mode is
undoable,useFormwill display a notification with a countdown to undo the mutation. useUpdatecallsdataProvider'supdatefunction and returns the response.useFormcallsonSuccessoronErrorfunction with the response, depending on the response status.- If the mutation fails,
useFormwill revert the query cache to the previous values made in step 3. - After a successful mutation,
useFormwill invalidate the queries specified ininvalidatesprop. onSuccessoronErrorfunction then calls theopenfunction of thenotificationProviderto inform the user.useFormredirects to thelistpage.
Clone
When useForm is mounted, it calls useOne hook to retrieve the record to be cloned. The id for the record is obtained from the props or the current route.
After form is submitted:
useFormcallsonFinishfunction with the form values.onFinishfunction callsuseCreatewith the form values.useUpdatecallsdataProvider'supdatefunction and returns the response.useFormcallsonSuccessoronErrorfunction with the response, depending on the response status.- After a successful mutation,
useFormwill invalidate the queries specified ininvalidatesprop. onSuccessoronErrorfunction then calls theopenfunction of thenotificationProviderto inform the user.useFormredirects to thelistpage.
resource Router IntegratedThis value can be inferred from the route. Click to see the guide for more information.
The resource name or identifier that will be used for the form. If not specified, it will be determined by the current route.
useForm({ resource: "products" });
id Router IntegratedThis value can be inferred from the route. Click to see the guide for more information.
The ID of the record that will be used for the action. If not specified, it will be determined by the current route. Required for edit and clone actions.
useForm({
id: 123,
});
If explicit resource is provided, id must be provided as well to avoid any unexpected API calls.
redirect Globally ConfigurableThis value can be configured globally. Click to see the guide for more information.
The redirection behavior after the form submission. It can be list, edit, show, create, or false. By default it will be list or whatever is defined in the Refine's global options.
useForm({ redirect: "show" });
This will only work if you have routerProvider defined in your <Refine> component along with the proper resource definition with routes and actions.
onMutationSuccess
Callback function to be called after a successful mutation. It will be called with the mutation result and variables.
useForm({
onMutationSuccess: (
data, // Mutation result, depending on the action its the response of `useCreate` or `useUpdate`
variables, // Variables/form values that were used for the mutation
context, // React Query's context for the mutation
isAutoSave, // Boolean value indicating if the mutation was triggered by auto-save or not
) => { ... }
});
onMutationError
Callback function to be called after a failed mutation. It will be called with the mutation error and variables.
useForm({
onMutationError: (
error, // Mutation error, depending on the action its the error response of `useCreate` or `useUpdate`
variables, // Variables/form values that were used for the mutation
context, // React Query's context for the mutation
isAutoSave, // Boolean value indicating if the mutation was triggered by auto-save or not
) => { ... }
});
invalidates
Determines the scope of the invalidation after a successful mutation. Can be array of list, many, detail, resourceAll, all or false. By default, create and clone actions will invalidate list and many. edit action will invalidate list, many and detail queries.
useForm({ invalidates: ["list", "many"] });
dataProviderName
Name of the data provider to be used in API interactions. Useful when there are more than one data providers defined.
useForm({ dataProviderName: "store" });
mutationMode Globally ConfigurableThis value can be configured globally. Click to see the guide for more information.
Behavior of the mutation, can either be pessimistic, optimistic or undoable. By default, pessimistic or whatever is defined in the Refine's global options.
useForm({ mutationMode: "optimistic" });
successNotification
NotificationProvideris required for this prop to work.
Customization options for the notification that will be shown after a successful mutation.
useForm({
// Can also be a static object if you don't need to access the data, values or resource.
// By setting it to `false`, you can disable the notification.
successNotification: (data, values, resource) => {
return {
message: `Successfully created ${data.title}`,
description: "good job!",
type: "success",
};
},
});
errorNotification
NotificationProvideris required for this prop to work.
Customization options for the notification that will be shown after a failed mutation.
useForm({
// Can also be a static object if you don't need to access the data, values or resource.
// By setting it to `false`, you can disable the notification.
errorNotification: (error, values, resource) => {
return {
message: `Failed to create ${values.title}`,
description: error.message,
type: "error",
};
},
});
meta Check the guideTo learn more about the `meta` and how it works with the data providers, refer to General Concepts guide
Additional information that will be passed to the data provider. Can be used to handle special cases in the data provider, generating GraphQL queries or handling additional parameters in the redirection routes.
useForm({ meta: { headers: { "x-greetings": "hello world" } } });
queryMeta
Meta data values to be used in the internal useOne call for the edit and clone actions. These values will take precedence over the meta values.
useForm({ queryMeta: { headers: { "x-greetings": "hello mars" } } });
mutationMeta
Meta data values to be used in the internal useCreate and useUpdate calls for form submissions. These values will take precedence over the meta values.
useForm({ mutationMeta: { headers: { "x-greetings": "hello pluto" } } });
queryOptions
Options to be used in the internal useOne call for the edit and clone actions.
useForm({
queryOptions: { retry: 3 },
});
createMutationOptions
Options to be used in the internal useCreate call for the create and clone actions.
useForm({
createMutationOptions: { retry: 3 },
});
updateMutationOptions
Options to be used in the internal useUpdate call for the edit action.
useForm({
updateMutationOptions: { retry: 3 },
});
liveMode
LiveProvideris required for this prop to work.
Behavior of how to handle received real-time updates, can be auto, manual or off. By default, auto or whatever is defined in the Refine's global options.
useForm({ liveMode: "auto" });
onLiveEvent
A callback function that will be called when a related real-time event is received.
useForm({
onLiveEvent: (event) => {
console.log(event);
},
});
liveParams
Additional parameters to be used in the liveProvider's subscribe method.
useForm({
liveParams: {
foo: "bar",
},
});
overtimeOptions
A set of options to be used for the overtime loading state. Useful when the API is slow to respond and a visual feedback is needed to indicate that the request is still in progress. overtimeOptions accept interval as number to determine the ticking intervals and onInterval to be called on each tick. useForm also returns overtime object with elapsedTime value that can be used for the feedback.
const { overtime } = useForm({
interval: 1000,
onInterval(elapsedTime) {
console.log(elapsedTime);
},
});
autoSave
Auto-save behavior of the form. Can have enabled to toggle auto-save, debounce to set the debounce interval for saving and invalidateOnUnmount to invalidate the queries specified in invalidates prop on unmount. This feature is only available for the edit action. By default, autoSave is disabled.
const { onFinishAutoSave } = useForm({
autoSave: {
enabled: true, // default is false
debounce: 2000, // debounce interval for auto-save, default is 1000
invalidateOnUnmount: true, // whether to invalidate the queries on unmount, default is false
},
});
Core implementation of the useForm hook doesn't provide out of the box auto-save functionality since it doesn't have access to the form values but it provides the necessary props and callbacks to implement it.
Extended versions of useForm (such as the one in @refinedev/react-hook-form) provides auto-save functionality out of the box.
optimisticUpdateMap
In optimistic and undoable mutation modes, useForm will automatically update the query cache with the form values immediately after the mutation is triggered. This behavior can be customized for each query set (list, many and detail queries) using optimisticUpdateMap.
useForm({
optimisticUpdateMap: {
// A boolean value can also be used to enable/disable the optimistic updates for the query set.
list: (
previous, // Previous query data
variables, // Variables used in the query
id, // Record id
) => { ... },
many: (
previous, // Previous query data
variables, // Variables used in the query
id, // Record id
) => { ... },
detail: (
previous, // Previous query data
variables, // Variables used in the query
) => { ... },
}
})
Return Values
onFinish
A function to call to trigger the mutation. Depending on the action, it will trigger the mutation of useCreate or useUpdate hooks.
const { onFinish } = useForm({ ... });
return (
<form onSubmit={() => onFinish(values)}>
{ /* ... */ }
</form>
);
onFinishAutoSave
A function to call to trigger the auto-save mutation. It will trigger the mutation of useUpdate hook. This will not trigger the formLoading state.
const { onFinishAutoSave } = useForm({ ... });
React.useEffect(() => {
// trigger auto-save on form values change, it will be debounced by the `autoSave.debounce` value
onFinishAutoSave(values);
}, [values]);
formLoading
A boolean value indicating the loading state of the form. It will reflect the loading status of the mutation or the query in edit and clone actions.
const { formLoading } = useForm({ ... });
mutation
Result of the mutation triggered by calling onFinish. Depending on the action, it will be the result of useCreate or useUpdate hooks. The mutation state can be accessed through mutation.isLoading, mutation.data, mutation.error, etc.
const { mutation } = useForm({ ... });
// Access mutation state
if (mutation.isLoading) {
// Handle loading state
}
if (mutation.isError) {
console.error(mutation.error);
}
if (mutation.isSuccess) {
console.log("Form submitted successfully:", mutation.data);
}
query
In edit and clone actions, result of the query of a record. It will be the result of useOne hook.
const { query: { data, error, isLoading } } = useForm({ ... });
setId
A setter function to set the id value. Useful when you want to change the id value after the form is mounted.
const { setId } = useForm({ ... });
const onItemSelect = (id) => {
setId(id);
};
redirect
A function to handle custom redirections, it accepts redirect and id parameters. redirect can be list, edit, show, create or false. id is the record id if needed in the specified redirect route.
const { redirect } = useForm({ ... });
redirect("show", 123);
overtime
An object with elapsedTime value that can be used for the overtime loading feedback.
const { overtime: { elapsedTime } } = useForm({ ... });
autoSaveProps
An object with data, error and status values that can be used for the auto-save feedback. data will be the result of the auto-save mutation, error will be the error of the auto-save mutation and status will be the status of the auto-save mutation.
const { autoSaveProps: { data, error, status } } = useForm({ ... });
API Reference
Properties
| Property | Type | Description | Default |
|---|---|---|---|
resource | | Resource name for API data interactions | Resource name that it reads from route |
id | Record id for fetching | Id that it reads from the URL | |
redirect |
| Page to redirect after a succesfull mutation |
|
meta | | Metadata query for dataProvider | |
queryMeta | | Metadata to pass for the | |
mutationMeta | | Metadata to pass for the mutation ( | |
mutationMode | |
| |
onMutationSuccess | | Called when a mutation is successful | |
onMutationError | | Called when a mutation encounters an error | |
undoableTimeout | | Duration to wait before executing mutations when |
|
dataProviderName | | If there is more than one | |
invalidates |
| You can use it to manage the invalidations that will occur at the end of the mutation. |
|
queryOptions | | react-query's useQuery options of useOne hook used while in edit mode. | |
createMutationOptions | | react-query's useMutation options of useCreate hook used while submitting in create and clone modes. | |
updateMutationOptions | | react-query's useMutation options of useUpdate hook used while submitting in edit mode. | |
optimisticUpdateMap | | If you customize the | { list: true, many: true, detail: true, } |
successNotification | | Success notification configuration to be displayed when the mutation is successful. | '"There was an error creating resource (status code: |
errorNotification | | Error notification configuration to be displayed when the mutation fails. | '"There was an error creating resource (status code: |
action | | Type of the form mode | Action that it reads from route otherwise "create" is used |
liveMode | Whether 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. |
| |
onLiveEvent | Callback to handle all related live events of this hook. |
| |
liveParams | Params to pass to liveProvider's subscribe method if liveMode is enabled. |
| |
overtimeOptions | | ||
autoSave | |
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.
Type Parameters
| Property | Description | Type | Default |
|---|---|---|---|
| TQueryFnData | Result data returned by the query function. Extends [BaseRecord][baserecord] | [BaseRecord][baserecord] | [BaseRecord][baserecord] |
| TError | Custom error object that extends [HttpError][httperror] | [HttpError][httperror] | [HttpError][httperror] |
| TVariables | Values for params. | {} | |
| TData | Result data returned by the select function. Extends [BaseRecord][baserecord]. If not specified, the value of TQueryFnData will be used as the default value. | [BaseRecord][baserecord] | TQueryFnData |
| TResponse | Result data returned by the mutation function. Extends [BaseRecord][baserecord]. If not specified, the value of TData will be used as the default value. | [BaseRecord][baserecord] | TData |
| TResponseError | Custom error object that extends [HttpError][httperror]. If not specified, the value of TError will be used as the default value. | [HttpError][httperror] | TError |
Return values
| Property | Description | Type |
|---|---|---|
| onFinish | Triggers the mutation | (values: TVariables) => Promise<CreateResponse<TData> | UpdateResponse<TData> | void> |
| query | Result of the query of a record | QueryObserverResult<TData, TError> |
| mutation | Result of the mutation triggered by calling onFinish | UseMutationResult<T> |
| formLoading | Loading state of form request | boolean |
| id | Record id for clone and create action | BaseKey |
| setId | id setter | Dispatch<SetStateAction< string | number | undefined>> |
| redirect | Redirect function for custom redirections | (redirect: "list"|"edit"|"show"|"create"| false ,idFromFunction?: BaseKey|undefined) => data |
| overtime | Overtime loading props | { elapsedTime?: number } |
| autoSaveProps | Auto save props | { data: UpdateResponse<TData> | undefined, error: HttpError | null, status: "loading" | "error" | "idle" | "success" } |
- Usage
- Parameters
- action
- Create
- Edit
- Clone
- resource
- id
- redirect
- onMutationSuccess
- onMutationError
- invalidates
- dataProviderName
- mutationMode
- successNotification
- errorNotification
- meta
- queryMeta
- mutationMeta
- queryOptions
- createMutationOptions
- updateMutationOptions
- liveMode
- onLiveEvent
- liveParams
- overtimeOptions
- autoSave
- optimisticUpdateMap
- Return Values
- onFinish
- onFinishAutoSave
- formLoading
- mutation
- query
- setId
- redirect
- overtime
- autoSaveProps
- API Reference
- Properties
- Type Parameters
- Return values
