import { OutlinedInputProps } from '@mui/material'; import { FormikConfig, FormikValues, useFormik } from 'formik'; import { isEqual, isObject } from 'lodash'; import { useCallback, useMemo } from 'react'; import debounce from '../lib/debounce'; import getFormikErrorMessages from '../lib/getFormikErrorMessages'; const isChainEqual = ( chain: string[], current: Tree, initial: Tree, ): 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, b as Tree); } return !isEqual(a, b); }; const useFormikUtils = ( formikConfig: FormikConfig, ): FormikUtils => { const formik = useFormik({ ...formikConfig }); const getFieldChanged = useCallback( (field: string) => { const parts = field.split('.'); return isChainEqual(parts, formik.values, formik.initialValues); }, [formik.initialValues, formik.values], ); const disableAutocomplete = useCallback( (overwrite?: Partial): OutlinedInputProps => ({ readOnly: true, onFocus: (event) => { event.target.readOnly = false; }, ...overwrite, }), [], ); const debounceHandleChange = useMemo( () => debounce(formik.handleChange), [formik.handleChange], ); const disabledSubmit = useMemo( () => !formik.dirty || !formik.isValid || formik.isValidating || formik.isSubmitting, [formik.dirty, formik.isSubmitting, formik.isValid, formik.isValidating], ); const formikErrors = useMemo( () => getFormikErrorMessages(formik.errors, { skip: (field) => !getFieldChanged(field), }), [formik.errors, getFieldChanged], ); return { disableAutocomplete, disabledSubmit, formik, formikErrors, handleChange: debounceHandleChange, }; }; export default useFormikUtils;