Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | 105x 105x 105x 154x 105x 105x 9x 8x 8x 8x 8x 8x 8x 1x 105x 102x 105x 30x 30x 4x 105x | import {
useForm,
UseFormReturn,
SubmitHandler,
FieldValues,
Resolver,
DefaultValues,
DeepPartial,
DeepMap,
} from "react-hook-form";
import { useImperativeHandle, ForwardedRef, useEffect } from "react";
import { IStep } from "@features/GiveOnboarding/types";
import {
IOnSubmitOptions,
StepHandle,
} from "@features/GiveOnboarding/types/handlers";
import { setStepClean, setStepDirty } from "@redux/slices/onboardingWizard";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "@redux/types/store";
interface UseFormStepConfig<TFormValues extends FieldValues> {
data_key: IStep;
resolver: Resolver<TFormValues, any>;
defaultValues?: DefaultValues<TFormValues>;
context?: any;
onSubmitStepData: (
formData: {
data: TFormValues;
dirtyFields: Partial<
Readonly<DeepMap<DeepPartial<TFormValues>, boolean>>
>;
},
onParentValid?: SubmitHandler<TFormValues>, // Callback to signal success to parent
onParentInvalid?: (error: any) => void, // Callback to signal failure (form or API) to parent
) => Promise<void> | void;
}
export function useFormStep<TFormValues extends FieldValues>(
ref: ForwardedRef<StepHandle<TFormValues>>,
config: UseFormStepConfig<TFormValues>,
): {
methods: UseFormReturn<TFormValues>;
submit: (options: IOnSubmitOptions<TFormValues>) => Promise<void>;
} {
const dispatch = useDispatch();
const { data_key, resolver, defaultValues, onSubmitStepData } = config;
const isDirty = useSelector(
(state: RootState) => state.onboardingWizard.dirtySteps[data_key],
);
const methods = useForm<TFormValues>({
mode: "onSubmit",
resolver,
defaultValues,
context: config.context,
});
const submit = ({
onInvalid,
onSubmitValidated,
onValid,
}: IOnSubmitOptions<TFormValues>) =>
methods.handleSubmit(
async (formData) => {
onSubmitValidated?.();
// onSubmitStepData will call parentOnValid or parentOnInvalid
// after its own logic (e.g., API call)
const dirtyFields = Object.keys(methods.formState.dirtyFields);
Iif (dirtyFields.length === 0 && !isDirty) {
onValid?.(formData); //is in edit form and the user did not changed any field
return;
}
try {
await onSubmitStepData(
{ data: formData, dirtyFields: methods.formState.dirtyFields },
onValid,
onInvalid,
);
dispatch(setStepClean(data_key));
} catch (e) {
// Catch any unhandled errors from onSubmitStepData
onInvalid?.({ type: "api", error: e });
}
},
(formErrors) => {
// Handle react-hook-form validation errors
onInvalid?.({ type: "formValidation", error: formErrors });
},
)();
useImperativeHandle(
ref,
() => ({
data_key,
methods,
submit: submit, // Immediately invoke handleSubmit
}),
[methods, data_key, onSubmitStepData],
);
useEffect(() => {
return () => {
if (methods.formState.isDirty) {
dispatch(setStepDirty(data_key));
}
};
}, []);
return { methods, submit };
}
|