1
2import { zodResolver } from "@hookform/resolvers/zod";
3import { UseMutationResult } from "@tanstack/react-query";
4import { useEffect } from "react";
5import { FieldValues, SubmitHandler, useForm } from "react-hook-form";
6import { ZodSchema } from "zod";
7
8interface IMutationDialog<TData, TError = unknown, TVariables = unknown> {
9 entity: TData | null
10 onOpenChange: (s: boolean) => void
11 createMutation: UseMutationResult<TData, TError, TVariables>
12 updateMutation: UseMutationResult<TData, TError, TVariables>
13 schema: ZodSchema
14 getDefaultValue: () => FieldValues
15 loadFormtter?: (d: FieldValues) => FieldValues
16}
17
18/** useMutationDialog - Dialog mutation hook
19 *
20 * @param entity - Entity value for update, leave empty for creation
21 * @param createMutation - react-query mutation hook for entity creation
22 * @param updateMutation - react-query mutation hook for entity edition
23 * @param entity - Entity value for update, leave empty for creation
24 * @param schema - Entity schema validated by zodResolver
25 * @param getDefaultValue - Default value function return the default value for function
26 * - Must be wrapped in useCallback to avoid infinite useEffect loop
27 * @param onOpenChange - Dialog state callback to change the dialog openness state
28 *
29 * @returns react-hook-form and submit handler
30 *
31 * @template TData - Mutation Type of Schema
32 * @template TError = unknown
33 * @template TVariables = unknown
34 */
35
36export const useMutationDialog = <TData, TError = unknown, TVariables = unknown>({
37 entity,
38 onOpenChange,
39 createMutation,
40 updateMutation,
41 schema,
42 getDefaultValue,
43 loadFormtter = (s) => s,
44}: IMutationDialog<TData, TError, TVariables>) => {
45
46 const form = useForm({
47 defaultValues: entity ? loadFormtter(entity) : getDefaultValue(),
48 resolver: zodResolver(schema)
49 });
50
51 const submit: SubmitHandler<FieldValues> = async (values) => {
52 if (entity) {
53 updateMutation.mutate(values as TVariables);
54 } else {
55 createMutation.mutate(values as TVariables);
56 }
57 onOpenChange(false);
58 };
59
60 useEffect(() => {
61 if (entity) {
62 form.reset(loadFormtter(entity))
63 } else {
64 form.reset(getDefaultValue());
65 }
66 // eslint-disable-next-line
67 }, [entity, form, getDefaultValue]);
68
69 const handleSubmit = form.handleSubmit(submit as SubmitHandler<FieldValues>);
70
71 return {
72 form,
73 handleSubmit
74 };
75};