diff --git a/.changeset/sour-cats-rescue.md b/.changeset/sour-cats-rescue.md new file mode 100644 index 000000000..c51e61732 --- /dev/null +++ b/.changeset/sour-cats-rescue.md @@ -0,0 +1,5 @@ +--- +'formik': major +--- + +validateOnMount initialize isValid to false until validation performed diff --git a/packages/formik/src/Formik.tsx b/packages/formik/src/Formik.tsx index 80d868665..f2ac8b2c6 100755 --- a/packages/formik/src/Formik.tsx +++ b/packages/formik/src/Formik.tsx @@ -152,6 +152,7 @@ export function useFormik({ const initialTouched = React.useRef(props.initialTouched || emptyTouched); const initialStatus = React.useRef(props.initialStatus); const isMounted = React.useRef(false); + const validated = React.useRef(false); const fieldRegistry = React.useRef({}); if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks @@ -314,6 +315,7 @@ export function useFormik({ props.validationSchema ? runValidationSchema(values) : {}, props.validate ? runValidateHandler(values) : {}, ]).then(([fieldErrors, schemaErrors, validateErrors]) => { + validated.current = true; const combinedErrors = deepmerge.all>( [fieldErrors, schemaErrors, validateErrors], { arrayMerge } @@ -949,15 +951,18 @@ export function useFormik({ ); const isValid = React.useMemo( - () => - typeof isInitialValid !== 'undefined' - ? dirty - ? state.errors && Object.keys(state.errors).length === 0 - : isInitialValid !== false && isFunction(isInitialValid) - ? (isInitialValid as (props: FormikConfig) => boolean)(props) - : (isInitialValid as boolean) - : state.errors && Object.keys(state.errors).length === 0, - [isInitialValid, dirty, state.errors, props] + () => { + if (!validated.current && validateOnMount) return false + if (typeof isInitialValid !== 'undefined') { + if (dirty) return state.errors && Object.keys(state.errors).length === 0; + + return isInitialValid !== false && isFunction(isInitialValid) + ? (isInitialValid as (props: FormikConfig) => boolean)(props) + : (isInitialValid as boolean) + } + return state.errors && Object.keys(state.errors).length === 0 + }, + [isInitialValid, dirty, state.errors, props, validated.current] ); const ctx = { diff --git a/packages/formik/test/Formik.test.tsx b/packages/formik/test/Formik.test.tsx index 98b6ef3c9..ded1b9941 100644 --- a/packages/formik/test/Formik.test.tsx +++ b/packages/formik/test/Formik.test.tsx @@ -128,6 +128,19 @@ describe('', () => { expect(props.submitCount).toBe(0); }); + it('should initialize isValid to false if validateOnMount until validation performed', async () => { + const validate = jest.fn(() => Promise.resolve()); + + const { getProps } = renderFormik({ validateOnMount: true, validate }); + expect(getProps().isValid).toBe(false); + + await waitFor(() => { + expect(validate).toHaveBeenCalledTimes(1); + }); + + expect(getProps().isValid).toBe(true); + }); + describe('handleChange', () => { it('updates values based on name attribute', () => { const { getProps, getByTestId } = renderFormik();