diff --git a/src/__tests__/useForm/formState.test.tsx b/src/__tests__/useForm/formState.test.tsx index 2f4724fccca..f0db9b9d099 100644 --- a/src/__tests__/useForm/formState.test.tsx +++ b/src/__tests__/useForm/formState.test.tsx @@ -10,6 +10,7 @@ import { act, renderHook } from '@testing-library/react-hooks'; import { VALIDATION_MODE } from '../../constants'; import { Controller } from '../../controller'; +import { useFieldArray } from '../../useFieldArray'; import { useForm } from '../../useForm'; describe('formState', () => { @@ -260,4 +261,154 @@ describe('formState', () => { screen.getByText('isSubmitted'); screen.getByText('isNotSubmitSuccessful'); }); + + it('should update correct isValid formState with dynamic fields', async () => { + const Component = () => { + const { + register, + control, + formState: { isValid }, + } = useForm<{ + list: { + firstName: string; + lastName: string; + }[]; + test: string; + test1: string; + test2: string; + test3: string; + }>({ + mode: 'onChange', + }); + const { append, fields } = useFieldArray({ + control, + name: 'list', + }); + + return ( +
+ ( + + )} + name={'test'} + rules={{ required: true }} + control={control} + defaultValue={''} + /> + + + ( + + )} + name={'test3'} + control={control} + defaultValue={''} + /> + {fields.map((field, index) => { + return ( +
+ ( + + )} + name={`list.${index}.firstName` as const} + control={control} + rules={{ required: true }} + defaultValue={field.firstName} + /> + +
+ ); + })} + +

{isValid ? 'valid' : 'inValid'}

+ + ); + }; + + render(); + + screen.getByText('inValid'); + + fireEvent.change(screen.getByPlaceholderText('test'), { + target: { value: '1' }, + }); + fireEvent.change(screen.getByPlaceholderText('test1'), { + target: { value: '1' }, + }); + + await waitFor(async () => { + screen.getByText('valid'); + }); + + fireEvent.click(screen.getByRole('button')); + + await waitFor(async () => { + screen.getByText('inValid'); + }); + + fireEvent.change(screen.getByPlaceholderText('list.0.firstName'), { + target: { value: '1' }, + }); + fireEvent.change(screen.getByPlaceholderText('list.0.lastName'), { + target: { value: '1' }, + }); + + await waitFor(async () => { + screen.getByText('valid'); + }); + + fireEvent.change(screen.getByPlaceholderText('list.0.lastName'), { + target: { value: '' }, + }); + + await waitFor(async () => { + screen.getByText('inValid'); + }); + + fireEvent.change(screen.getByPlaceholderText('list.0.lastName'), { + target: { value: '1' }, + }); + + await waitFor(async () => { + screen.getByText('valid'); + }); + + fireEvent.change(screen.getByPlaceholderText('list.0.firstName'), { + target: { value: '' }, + }); + + await waitFor(async () => { + screen.getByText('inValid'); + }); + + fireEvent.change(screen.getByPlaceholderText('list.0.firstName'), { + target: { value: '1' }, + }); + + await waitFor(async () => { + screen.getByText('valid'); + }); + }); }); diff --git a/src/useForm.ts b/src/useForm.ts index 0b879e47d13..60667c192e3 100644 --- a/src/useForm.ts +++ b/src/useForm.ts @@ -170,10 +170,13 @@ export function useForm< contextRef.current = context; resolverRef.current = resolver; - const getIsValid = () => - (formStateRef.current.isValid = + const getIsValid = () => { + formStateRef.current.isValid = deepEqual(validFieldsRef.current, fieldsWithValidationRef.current) && - isEmptyObject(formStateRef.current.errors)); + isEmptyObject(formStateRef.current.errors); + + return formStateRef.current.isValid; + }; const shouldRenderBaseOnError = React.useCallback( (