How to autosave?

I'd like to create a form that autosaves the changed content to my data provider. How do I best achieve this within the refine framework?
10 Replies
kapa.ai
kapa.ai2y ago
Hi @imedemi. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To create a form that autosaves the changed content to your data provider within the refine framework, you can use the useForm hook and the saveButtonProps variable. Here's an example of how to achieve this: 1. Import the necessary components and hooks:
import { useForm } from "@refinedev/react-hook-form";
import { Edit } from "@refinedev/mui";
import { Box, TextField } from "@mui/material";
import { useForm } from "@refinedev/react-hook-form";
import { Edit } from "@refinedev/mui";
import { Box, TextField } from "@mui/material";
2. Use the useForm hook and destructure the saveButtonProps variable:
const {
refineCore: { onFinish, formLoading },
register,
handleSubmit,
formState: { errors },
} = useForm<ICategory>();
const {
refineCore: { onFinish, formLoading },
register,
handleSubmit,
formState: { errors },
} = useForm<ICategory>();
3. Pass the saveButtonProps to the <SaveButton> component, which includes submitting the form action, button loading, and disable states:
<Edit
isLoading={formLoading}
saveButtonProps={{ onClick: handleSubmit(onFinish) }}
>
<Box component="form">
<TextField
{...register("title", { required: true })}
error={!!errors?.title}
helperText={errors?.title?.message}
margin="normal"
required
fullWidth
id="title"
label="Title"
name="title"
defaultValue={" "}
/>
</Box>
</Edit>
<Edit
isLoading={formLoading}
saveButtonProps={{ onClick: handleSubmit(onFinish) }}
>
<Box component="form">
<TextField
{...register("title", { required: true })}
error={!!errors?.title}
helperText={errors?.title?.message}
margin="normal"
required
fullWidth
id="title"
label="Title"
name="title"
defaultValue={" "}
/>
</Box>
</Edit>
In this example, the form will submit the changes to your data provider when the save button is clicked. If you want to autosave the form without clicking the save button, you can add an onChange event to the form fields and trigger the handleSubmit(onFinish) function when the content changes. For more information, refer to the SaveButton documentation (https://refine.dev/docs/api-reference/mui/components/buttons/save-button). Keep in mind that this example uses MUI components, but you can achieve similar functionality with Ant Design components if you prefer. Please note that the provided code snippets are based on the information extracts. Make sure to adapt them to your specific use case and refine setup. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2y ago
<@711407420718579741> kapa.ai is still learning and improving, please let us how it did by reacting below
yappiest-sapphire
yappiest-sapphire2y ago
Hey @imedemi, Which UI framework are you using?
automatic-azure
automatic-azure2y ago
Material UI.
yappiest-sapphire
yappiest-sapphire2y ago
Can you try like this:
const {
refineCore: { onFinish },
handleSubmit,
watch
} = useForm({
refineCoreProps: {
redirect: false
}
});

React.useEffect(() => {
const subscription = watch(handleSubmit(onFinish));
return () => subscription.unsubscribe();
}, [handleSubmit, onFinish, watch]);
const {
refineCore: { onFinish },
handleSubmit,
watch
} = useForm({
refineCoreProps: {
redirect: false
}
});

React.useEffect(() => {
const subscription = watch(handleSubmit(onFinish));
return () => subscription.unsubscribe();
}, [handleSubmit, onFinish, watch]);
automatic-azure
automatic-azure2y ago
I get a typescript error for const subscription = watch(handleSubmit(onFinish));
TS2769: No overload matches this call.   Overload 1 of 4, '(names: readonly string[], defaultValue?: { [x: string]: any; } | undefined): readonly any[]', gave the following error.     Argument of type '(e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>' is not assignable to parameter of type 'readonly string[]'.   Overload 2 of 4, '(name: string, defaultValue?: any): any', gave the following error.     Argument of type '(e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>' is not assignable to parameter of type 'string'.   Overload 3 of 4, '(callback: WatchObserver<FieldValues>, defaultValues?: { [x: string]: any; } | undefined): Subscription', gave the following error.     Argument of type '(e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>' is not assignable to parameter of type 'WatchObserver<FieldValues>'.       Types of parameters 'e' and 'value' are incompatible.         Type '{ [x: string]: any; }' is missing the following properties from type 'BaseSyntheticEvent<object, any, any>': nativeEvent, currentTarget, target, bubbles, and 11 more.
TS2769: No overload matches this call.   Overload 1 of 4, '(names: readonly string[], defaultValue?: { [x: string]: any; } | undefined): readonly any[]', gave the following error.     Argument of type '(e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>' is not assignable to parameter of type 'readonly string[]'.   Overload 2 of 4, '(name: string, defaultValue?: any): any', gave the following error.     Argument of type '(e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>' is not assignable to parameter of type 'string'.   Overload 3 of 4, '(callback: WatchObserver<FieldValues>, defaultValues?: { [x: string]: any; } | undefined): Subscription', gave the following error.     Argument of type '(e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>' is not assignable to parameter of type 'WatchObserver<FieldValues>'.       Types of parameters 'e' and 'value' are incompatible.         Type '{ [x: string]: any; }' is missing the following properties from type 'BaseSyntheticEvent<object, any, any>': nativeEvent, currentTarget, target, bubbles, and 11 more.
yappiest-sapphire
yappiest-sapphire2y ago
You can use generic types of useForm hook to resolve errors. You can see an example here: https://github.com/refinedev/refine/blob/next/examples/form-mui-use-form/src/pages/posts/edit.tsx#L17-L22
automatic-azure
automatic-azure2y ago
Thank you, @salihozdemir. I finally got it working and it works like a charm! I needed to also make the page be an "edit" resource instead of "show" in order to update the existing resource instead of creating a new one. Now I just need to turn off the "Successfully updated notification and it will be behaving as I expect for an auto-save.
yappiest-sapphire
yappiest-sapphire2y ago
You can disable notifications with successNotification and errorNotification props. Just set the false them.
automatic-azure
automatic-azure2y ago
Yay! That worked. Thank you. For others following this thread, successNotification seems like it needs to be defined from within the refineCoreProps.
const {
saveButtonProps,
refineCore: {queryResult, onFinish, formLoading},
handleSubmit,
register,
watch
} = useForm({
refineCoreProps: {
redirect: false,
successNotification: false,
},
});
const {
saveButtonProps,
refineCore: {queryResult, onFinish, formLoading},
handleSubmit,
register,
watch
} = useForm({
refineCoreProps: {
redirect: false,
successNotification: false,
},
});