Skip to content

Commit

Permalink
🐞 fix react-hook-form#10139 useFieldArray array error not updating …
Browse files Browse the repository at this point in the history
…in some cases (react-hook-form#10150)

* Add test for useFieldArray array error updating on array change

* Fix useFieldArray array error not updating sometimes (react-hook-form#10139)
  • Loading branch information
kylemclean committed Mar 21, 2023
1 parent 20d897b commit c1cb992
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 12 deletions.
161 changes: 161 additions & 0 deletions src/__tests__/useFieldArray.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,167 @@ describe('useFieldArray', () => {
expect(screen.queryByText('minLength')).not.toBeInTheDocument(),
);
});

it('should update error when array is changed', async () => {
const App = () => {
const {
register,
control,
formState: { errors },
} = useForm<{
test: { value: string }[];
}>({
mode: 'onChange',
resolver: (data) => {
const errors: { test?: any } = {};
if (data.test.length > 4) {
errors.test = { type: 'toobig', message: 'WAY too many items' };
} else if (data.test.length > 3) {
errors.test = { type: 'toobig', message: 'Too many items' };
}
for (const [index, item] of data.test.entries()) {
if (item.value === '') {
errors.test = errors.test || [];
errors.test[index] = {
value: { type: 'required', message: 'Required' },
};
}
}

return {
values: data,
errors,
};
},
defaultValues: {
test: [{ value: '0' }, { value: '1' }, { value: '2' }],
},
});
const { fields, append, remove } = useFieldArray({
name: 'test',
control,
});

return (
<form>
{errors.test?.type && <p>Array error: {errors.test.message}</p>}
{fields.map((item, i) => (
<div key={item.id}>
<input {...register(`test.${i}.value` as const)} />
<button type="button" onClick={() => remove(i)}>
remove
</button>
{errors.test?.[i]?.value && (
<span>
Item {i} error: {errors.test?.[i]?.value?.message}
</span>
)}
</div>
))}
<button
type="button"
onClick={() =>
append({
value: fields.length.toString(),
})
}
>
append
</button>
</form>
);
};

render(<App />);

await waitFor(() =>
expect(screen.queryByText('Array error:')).not.toBeInTheDocument(),
);

fireEvent.click(screen.getByRole('button', { name: 'append' }));

await waitFor(() =>
expect(
screen.queryByText('Array error: Too many items'),
).toBeInTheDocument(),
);

fireEvent.click(screen.getByRole('button', { name: 'append' }));

await waitFor(() =>
expect(
screen.queryByText('Array error: WAY too many items'),
).toBeInTheDocument(),
);

fireEvent.click(screen.getAllByRole('button', { name: 'remove' })[0]);

await waitFor(() =>
expect(
screen.queryByText('Array error: Too many items'),
).toBeInTheDocument(),
);

fireEvent.click(screen.getAllByRole('button', { name: 'remove' })[0]);

await waitFor(() =>
expect(screen.queryByText('Array error:')).not.toBeInTheDocument(),
);

fireEvent.change(screen.getAllByRole('textbox')[0], {
target: { value: '' },
});

await waitFor(() =>
expect(
screen.queryByText('Item 0 error: Required'),
).toBeInTheDocument(),
);

fireEvent.click(screen.getByRole('button', { name: 'append' }));

await waitFor(() => {
expect(
screen.queryByText('Array error: Too many items'),
).toBeInTheDocument();
expect(
screen.queryByText('Item 0 error: Required'),
).toBeInTheDocument();
});

fireEvent.click(screen.getByRole('button', { name: 'append' }));

await waitFor(() => {
expect(
screen.queryByText('Array error: WAY too many items'),
).toBeInTheDocument();
expect(
screen.queryByText('Item 0 error: Required'),
).toBeInTheDocument();
});

fireEvent.click(screen.getAllByRole('button', { name: 'remove' })[4]);

await waitFor(() => {
expect(
screen.queryByText('Array error: Too many items'),
).toBeInTheDocument();
expect(
screen.queryByText('Item 0 error: Required'),
).toBeInTheDocument();
});

fireEvent.click(screen.getAllByRole('button', { name: 'remove' })[3]);

await waitFor(() => {
expect(
screen.queryByText('Array error: Too many items'),
).not.toBeInTheDocument();
expect(
screen.queryByText('Item 0 error: Required'),
).toBeInTheDocument();
});
});
});

describe('when component unMount', () => {
Expand Down
18 changes: 6 additions & 12 deletions src/useFieldArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,18 +323,12 @@ export function useFieldArray<
if (control._options.resolver) {
control._executeSchema([name]).then((result) => {
const error = get(result.errors, name);
const existingError = get(control._formState.errors, name);

if (
existingError ? !error && existingError.type : error && error.type
) {
error
? set(control._formState.errors, name, error)
: unset(control._formState.errors, name);
control._subjects.state.next({
errors: control._formState.errors as FieldErrors<TFieldValues>,
});
}
error
? set(control._formState.errors, name, error)
: unset(control._formState.errors, name);
control._subjects.state.next({
errors: control._formState.errors as FieldErrors<TFieldValues>,
});
});
} else {
const field: Field = get(control._fields, name);
Expand Down

0 comments on commit c1cb992

Please sign in to comment.