From 832f573140781ab8b383c3cd9c83f205ba43fedc Mon Sep 17 00:00:00 2001 From: Sum Tsui Date: Sun, 8 Dec 2024 12:18:12 +0800 Subject: [PATCH 1/2] fix: make validateField return type consistent --- .changeset/nine-adults-turn.md | 9 ++++++++ docs/api/formik.md | 2 +- packages/formik/src/Formik.tsx | 12 +++++----- packages/formik/src/types.tsx | 2 +- packages/formik/test/Field.test.tsx | 34 ++++++++++++++++------------- 5 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 .changeset/nine-adults-turn.md diff --git a/.changeset/nine-adults-turn.md b/.changeset/nine-adults-turn.md new file mode 100644 index 000000000..eb9df2129 --- /dev/null +++ b/.changeset/nine-adults-turn.md @@ -0,0 +1,9 @@ +--- +'formik': patch +--- + +The validateField function now consistently returns Promise across all validation paths, matching its type definition where `string` represents a validation error message, `undefined` represents a successful validation. + +while previous implementation: +- field-level validation returned Promise +- default case and schema validation returned Promise \ No newline at end of file diff --git a/docs/api/formik.md b/docs/api/formik.md index 9fa07f3a4..4bb638fb9 100644 --- a/docs/api/formik.md +++ b/docs/api/formik.md @@ -245,7 +245,7 @@ component. Imperatively call your `validate` or `validateSchema` depending on what was specified. You can optionally pass values to validate against and this modify Formik state accordingly, otherwise this will use the current `values` of the form. -#### `validateField: (field: string) => void` +#### `validateField: (field: string) => Promise` Imperatively call field's `validate` function if specified for given field or run schema validation using [Yup's `schema.validateAt`](https://github.com/jquense/yup#mixedvalidateatpath-string-value-any-options-object-promiseany-validationerror) and the provided top-level `validationSchema` prop. Formik will use the current field value. diff --git a/packages/formik/src/Formik.tsx b/packages/formik/src/Formik.tsx index ea36e80d3..435fb48f4 100755 --- a/packages/formik/src/Formik.tsx +++ b/packages/formik/src/Formik.tsx @@ -484,11 +484,10 @@ export function useFormik({ } }, [enableReinitialize, props.initialStatus, props.initialTouched]); - const validateField = useEventCallback((name: string) => { + const validateField = useEventCallback((name: string): Promise => { // This will efficiently validate a single field by avoiding state // changes if the validation function is synchronous. It's different from // what is called when using validateForm. - if ( fieldRegistry.current[name] && isFunction(fieldRegistry.current[name].validate) @@ -506,6 +505,7 @@ export function useFormik({ payload: { field: name, value: error }, }); dispatch({ type: 'SET_ISVALIDATING', payload: false }); + return error; }); } else { dispatch({ @@ -522,15 +522,17 @@ export function useFormik({ return runValidationSchema(state.values, name) .then((x: any) => x) .then((error: any) => { + const fieldError = getIn(error, name); dispatch({ type: 'SET_FIELD_ERROR', - payload: { field: name, value: getIn(error, name) }, + payload: { field: name, value: fieldError }, }); dispatch({ type: 'SET_ISVALIDATING', payload: false }); - }); + return fieldError + }) } - return Promise.resolve(); + return Promise.resolve(undefined); }); const registerField = React.useCallback((name: string, { validate }: any) => { diff --git a/packages/formik/src/types.tsx b/packages/formik/src/types.tsx index 71db6792c..adadad209 100644 --- a/packages/formik/src/types.tsx +++ b/packages/formik/src/types.tsx @@ -109,7 +109,7 @@ export interface FormikHelpers { /** Validate form values */ validateForm: (values?: any) => Promise>; /** Validate field value */ - validateField: (field: string) => Promise | Promise; + validateField: (field: string) => Promise; /** Reset form */ resetForm: (nextState?: Partial>) => void; /** Submit the form imperatively */ diff --git a/packages/formik/test/Field.test.tsx b/packages/formik/test/Field.test.tsx index 9103f982e..7d648a40e 100644 --- a/packages/formik/test/Field.test.tsx +++ b/packages/formik/test/Field.test.tsx @@ -385,15 +385,16 @@ describe('Field / FastField', () => { component: 'input', }); rerender(); - - act(() => { - getFormProps().validateField('name'); + + const error = await act(async () => { + return await getFormProps().validateField('name'); }); rerender(); await waitFor(() => { expect(validate).toHaveBeenCalled(); expect(getFormProps().errors.name).toBe('Error!'); + expect(error).toBe('Error!'); }); } ); @@ -408,12 +409,15 @@ describe('Field / FastField', () => { // workaround for `useEffect` to run: https://github.com/facebook/react/issues/14050 rerender(); - act(() => { - getFormProps().validateField('name'); + const error = await act(async () => { + return await getFormProps().validateField('name'); }); expect(validate).toHaveBeenCalled(); - await waitFor(() => expect(getFormProps().errors.name).toBe('Error!')); + await waitFor(() => { + expect(getFormProps().errors.name).toBe('Error!') + expect(error).toBe('Error!') + }); } ); @@ -432,15 +436,16 @@ describe('Field / FastField', () => { rerender(); - act(() => { - getFormProps().validateField('name'); + const error = await act(async () => { + return await getFormProps().validateField('name'); }); - await waitFor(() => + await waitFor(() => { expect(getFormProps().errors).toEqual({ name: errorMessage, }) - ); + expect(error).toBe(errorMessage) + }); } ); @@ -460,13 +465,12 @@ describe('Field / FastField', () => { rerender(); - act(() => { - getFormProps().validateField('user.name'); - }); + const error = await act(async () => await getFormProps().validateField('user.name')); - await waitFor(() => + await waitFor(() => { expect(getFormProps().errors).toEqual({ user: { name: 'required' } }) - ); + expect(error).toBe('required') + }); } ); }); From 4362233f715b950d821d8225c7cff2c1f9f56de2 Mon Sep 17 00:00:00 2001 From: Sum Tsui Date: Sun, 8 Dec 2024 12:35:45 +0800 Subject: [PATCH 2/2] chore: update changeset --- .changeset/nine-adults-turn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/nine-adults-turn.md b/.changeset/nine-adults-turn.md index eb9df2129..f8bac7f80 100644 --- a/.changeset/nine-adults-turn.md +++ b/.changeset/nine-adults-turn.md @@ -2,8 +2,8 @@ 'formik': patch --- -The validateField function now consistently returns Promise across all validation paths, matching its type definition where `string` represents a validation error message, `undefined` represents a successful validation. +The validateField function now consistently returns Promise across all validation paths, where `string` represents a validation error message, `undefined` represents a successful validation. while previous implementation: -- field-level validation returned Promise -- default case and schema validation returned Promise \ No newline at end of file +- validation via `` returned Promise +- validation via schema returned Promise \ No newline at end of file