Skip to content

Improve useFieldArray to allow async validation#187

Open
iamdey wants to merge 1 commit intofinal-form:masterfrom
iamdey:async_validator
Open

Improve useFieldArray to allow async validation#187
iamdey wants to merge 1 commit intofinal-form:masterfrom
iamdey:async_validator

Conversation

@iamdey
Copy link
Copy Markdown
Contributor

@iamdey iamdey commented Feb 3, 2026

In order to have similar API as in Asynchronous Field-Level Validation cf. https://final-form.org/docs/react-final-form/examples

This allows to get errors from an async function.

This PR is similar to #179 but with tests and non-breaking for sync validators tests.

I used this code for a couple of years now.

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation error handling consistency for field arrays across different error types
    • Added support for asynchronous validation in field arrays
  • Tests

    • Expanded test coverage for field array validation error scenarios

@iamdey iamdey requested a review from erikras as a code owner February 3, 2026 17:37
@erikras-richard-agent
Copy link
Copy Markdown
Contributor

Thanks for this contribution! 👍 I can see you've been using this in production for a couple of years.

What this adds:

  • Async validation support for useFieldArray (detects Promises and handles them)
  • Extracted handleError() helper to handle both string and array errors
  • Comprehensive tests for array errors, string errors, and Promise-based errors

Code review observations:
✅ Non-breaking - maintains backward compatibility with sync validators
✅ Good test coverage (3 new test cases)
✅ Clean implementation using Promise detection (validation?.then)

Questions/concerns:

  1. CI not run - This PR has no CI checks. Can you rebase against master to trigger CI (Lint, Prettier, Unit Tests)?

  2. Related to #509? - There's ongoing work on async validation race conditions in the core library (final-form #509). This PR is at the field-level validation layer. We should verify they don't conflict.

  3. Documentation - Should we update docs/examples to show async field array validation?

@erikras - This is a feature enhancement (not a bug fix) that enables async validation for field arrays, matching the API from regular fields. Needs CI before merge and consideration of interaction with #509 async validation work.

I'll hold off on approval until CI runs and we can verify no conflicts with ongoing async validation work. Would you like me to help with documentation if this gets merged?

@erikras
Copy link
Copy Markdown
Member

erikras commented Feb 10, 2026

Rebase, please?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

The changes enhance useFieldArray to support array-level validation errors from validators returning strings, arrays, or Promises. A new handleError utility normalizes error formats, and tests verify proper error propagation through field meta and form state.

Changes

Cohort / File(s) Summary
Array Error Handling Implementation
src/useFieldArray.ts
Introduces handleError utility to normalize validator errors (strings, arrays, Promises) into consistent ARRAY_ERROR format. Adds explicit mutator readiness check and refactors validation to support async validators via useMemo-wrapped mutators.
Error Handling Test Suite
src/useFieldArray.test.tsx
Adds comprehensive tests validating array error handling from validators returning strings, arrays, or Promises; verifies errors surface via field meta.error and form state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • erikras
  • erikras-richard-agent

Poem

🐰 Arrays now handle their errors with grace,
Promises resolved in the right place,
Validators speak—strings, arrays, or more,
The rabbit hops in to normalize lore! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improve useFieldArray to allow async validation' directly and specifically describes the main change—adding async validation support to useFieldArray.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@iamdey
Copy link
Copy Markdown
Contributor Author

iamdey commented Feb 24, 2026

Rebase, please?

I though you were talking to your bot

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/useFieldArray.test.tsx`:
- Around line 211-244: The test "should handle Promises errors" uses waitFor
without awaiting it, which can let the test finish before the async expectation
runs; update the test by awaiting the waitFor call that wraps the expectation so
the Promise returned by waitFor is resolved before the test completes (modify
the test body where waitFor(...) is called in the "should handle Promises
errors" spec that renders MyFieldArray and uses spy to assert useFieldArray's
meta.error).

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f231a9 and b3e2be5.

📒 Files selected for processing (2)
  • src/useFieldArray.test.tsx
  • src/useFieldArray.ts

Comment on lines +211 to +244
it('should handle Promises errors', async () => {
const spy = jest.fn()
const MyFieldArray = () => {
spy(
useFieldArray('names', {
validate: (values) => Promise.resolve(['await fail'])
})
)
return null
}
render(
<Form
onSubmit={onSubmitMock}
mutators={arrayMutators as any}
subscription={{}}
>
{() => (
<form>
<MyFieldArray />
</form>
)}
</Form>
)

waitFor(() =>
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
meta: expect.objectContaining({
error: ['await fail']
})
})
)
)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the test file
find . -name "useFieldArray.test.tsx" -type f

Repository: final-form/react-final-form-arrays

Length of output: 106


🏁 Script executed:

# Once found, check the file size and read the specific lines
wc -l src/useFieldArray.test.tsx

Repository: final-form/react-final-form-arrays

Length of output: 108


🏁 Script executed:

# Read lines 211-244 and surrounding context (with imports at top)
head -20 src/useFieldArray.test.tsx

Repository: final-form/react-final-form-arrays

Length of output: 850


🏁 Script executed:

# Read the specific test around lines 211-244
sed -n '205,250p' src/useFieldArray.test.tsx

Repository: final-form/react-final-form-arrays

Length of output: 949


Await waitFor to ensure the async assertion runs.
The test function is async but the waitFor call on line 235 is not awaited. Since waitFor returns a Promise, the test will complete before the expectation inside it runs, potentially causing false passes.

Fix
-    waitFor(() =>
+    await waitFor(() =>
       expect(spy).toHaveBeenCalledWith(
         expect.objectContaining({
           meta: expect.objectContaining({
             error: ['await fail']
           })
         })
       )
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should handle Promises errors', async () => {
const spy = jest.fn()
const MyFieldArray = () => {
spy(
useFieldArray('names', {
validate: (values) => Promise.resolve(['await fail'])
})
)
return null
}
render(
<Form
onSubmit={onSubmitMock}
mutators={arrayMutators as any}
subscription={{}}
>
{() => (
<form>
<MyFieldArray />
</form>
)}
</Form>
)
waitFor(() =>
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
meta: expect.objectContaining({
error: ['await fail']
})
})
)
)
})
it('should handle Promises errors', async () => {
const spy = jest.fn()
const MyFieldArray = () => {
spy(
useFieldArray('names', {
validate: (values) => Promise.resolve(['await fail'])
})
)
return null
}
render(
<Form
onSubmit={onSubmitMock}
mutators={arrayMutators as any}
subscription={{}}
>
{() => (
<form>
<MyFieldArray />
</form>
)}
</Form>
)
await waitFor(() =>
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
meta: expect.objectContaining({
error: ['await fail']
})
})
)
)
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/useFieldArray.test.tsx` around lines 211 - 244, The test "should handle
Promises errors" uses waitFor without awaiting it, which can let the test finish
before the async expectation runs; update the test by awaiting the waitFor call
that wraps the expectation so the Promise returned by waitFor is resolved before
the test completes (modify the test body where waitFor(...) is called in the
"should handle Promises errors" spec that renders MyFieldArray and uses spy to
assert useFieldArray's meta.error).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants