import { useState } from "react"; import { useDeepCompareEffect } from "ahooks"; import { Form, type FormInstance, type FormProps } from "antd"; export interface UseAntdFormOptions = any> { form?: FormInstance; initialValues?: Partial | (() => Partial | Promise>); onSubmit?: (values: T) => unknown; } export interface UseAntdFormReturns = any> { form: FormInstance; formProps: Omit, "children">; formPending: boolean; submit: (values?: T) => Promise; } /** * * @param {UseAntdFormOptions} options * @returns {UseAntdFormReturns} */ const useAntdForm = = any>({ initialValues, form, onSubmit }: UseAntdFormOptions): UseAntdFormReturns => { const formInst = form ?? Form["useForm"]()[0]; const [formInitialValues, setFormInitialValues] = useState>(); const [formPending, setFormPending] = useState(false); useDeepCompareEffect(() => { let unmounted = false; if (!initialValues) { return; } let temp: Promise>; if (typeof initialValues === "function") { temp = Promise.resolve(initialValues()); } else { temp = Promise.resolve(initialValues); } temp.then((temp) => { if (!unmounted) { type FieldName = Parameters["getFieldValue"]>[0]; type FieldsValue = Parameters["setFieldsValue"]>[0]; const obj = { ...temp }; Object.keys(temp).forEach((key) => { obj[key as keyof T] = formInst!.isFieldTouched(key as FieldName) ? formInst!.getFieldValue(key as FieldName) : temp[key as keyof T]; }); setFormInitialValues(temp); formInst!.setFieldsValue(obj as FieldsValue); } }); return () => { unmounted = true; }; }, [formInst, initialValues]); const onFinish = (values: T) => { if (formPending) return Promise.reject(new Error("Form is pending")); setFormPending(true); return new Promise((resolve, reject) => { formInst .validateFields() .then(() => { resolve( Promise.resolve(onSubmit?.(values)) .then((data) => { setFormPending(false); return data; }) .catch((err) => { setFormPending(false); throw err; }) ); }) .catch((err) => { setFormPending(false); reject(err); }); }); }; const formProps: FormProps = { form: formInst, initialValues: formInitialValues, onFinish, }; return { form: formInst, formProps: formProps, formPending: formPending, submit: () => { return onFinish(formInst.getFieldsValue(true)); }, }; }; export default useAntdForm;