You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
2.3 KiB
94 lines
2.3 KiB
import { FormikConfig, FormikValues, useFormik } from 'formik'; |
|
import { isEqual, isObject } from 'lodash'; |
|
import { useCallback, useMemo, useState } from 'react'; |
|
|
|
import debounce from '../lib/debounce'; |
|
import getFormikErrorMessages from '../lib/getFormikErrorMessages'; |
|
|
|
const isChainEqual = ( |
|
chain: string[], |
|
current: Tree<unknown>, |
|
initial: Tree<unknown>, |
|
): boolean => { |
|
const [part, ...remain] = chain; |
|
|
|
if (!(part in current)) { |
|
return false; |
|
} |
|
|
|
const a = current[part]; |
|
const b = initial[part]; |
|
|
|
if (isObject(a) && isObject(b) && remain.length) { |
|
return isChainEqual(remain, a as Tree<unknown>, b as Tree<unknown>); |
|
} |
|
|
|
return !isEqual(a, b); |
|
}; |
|
|
|
const useFormikUtils = <Values extends FormikValues = FormikValues>( |
|
formikConfig: FormikConfig<Values>, |
|
): FormikUtils<Values> => { |
|
const [changing, setChanging] = useState<boolean>(false); |
|
|
|
const formik = useFormik<Values>({ ...formikConfig }); |
|
|
|
const getFieldChanged = useCallback( |
|
(field: string) => { |
|
const parts = field.split('.'); |
|
|
|
return isChainEqual(parts, formik.values, formik.initialValues); |
|
}, |
|
[formik.initialValues, formik.values], |
|
); |
|
|
|
const debounceHandleChange = useMemo(() => { |
|
const base = debounce((...args: Parameters<typeof formik.handleChange>) => { |
|
formik.handleChange(...args); |
|
setChanging(false); |
|
}); |
|
|
|
return (...args: Parameters<typeof base>) => { |
|
setChanging(true); |
|
base(...args); |
|
}; |
|
|
|
// Only handle change is being used in the debounced function, no need to |
|
// add the whole formik object as dependency. |
|
// |
|
// eslint-disable-next-line react-hooks/exhaustive-deps |
|
}, [formik.handleChange]); |
|
|
|
const disabledSubmit = useMemo( |
|
() => |
|
changing || |
|
!formik.dirty || |
|
!formik.isValid || |
|
formik.isValidating || |
|
formik.isSubmitting, |
|
[ |
|
changing, |
|
formik.dirty, |
|
formik.isSubmitting, |
|
formik.isValid, |
|
formik.isValidating, |
|
], |
|
); |
|
|
|
const formikErrors = useMemo<Messages>( |
|
() => |
|
getFormikErrorMessages(formik.errors, { |
|
skip: (field) => !getFieldChanged(field), |
|
}), |
|
[formik.errors, getFieldChanged], |
|
); |
|
|
|
return { |
|
disabledSubmit, |
|
formik, |
|
formikErrors, |
|
handleChange: debounceHandleChange, |
|
}; |
|
}; |
|
|
|
export default useFormikUtils;
|
|
|