You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Validation correctness is fundamental. If users see inconsistent errors or validations are silently dropped, this can lead to incorrect form acceptance or incorrect UX and bugs in apps using Final Form.
Both issues manifest with async validators (very common for server-side uniqueness checks, availability checks, etc.).
Reproduction (POC)
I added a reproducible POC script at poc/async-validation-poc.ts. Two scenarios are provided:
scenario1_suppressedErrors demonstrates that two fields with validateFields: [] and independent async validators may end with one field's error missing.
scenario2_validatingFlag demonstrates overlapping validations on the same field where validating can be inconsistent.
Run the POC locally (recommended to use npx ts-node):
# from repository root
npx ts-node poc/async-validation-poc.ts
Each field's async validation should set the correct error state when its own validator finishes, regardless of concurrent validations on other fields.
If multiple async validators run for the same field, validating should remain true until all outstanding validations for that field have finished, and only then flip to false.
Observed behavior
Some validations' error values are not persisted/published after concurrent validations (suppression).
validating toggles too early in some workflows (flips to false while newer validations still in-flight), or subscriber notifications are missed or duplicated due to race conditions.
Root cause analysis (summary)
Historically, Final Form kept a single nextAsyncValidationKey for global async runs and used Promise resolution order + comparison with that key to decide whether to ignore a resolved result.
Field-level async validations were not tracked per-field sufficiently (no per-field counters/keys), so later runs could cause earlier ones to override or be ignored incorrectly.
The notification contract (field subscribers vs global form subscribers) expects precise ordering and counts of notifications; adding per-field merging without carefully scheduling notifications can cause duplicate or missing notifications or re-entrancy into the validation loop.
Suggested fixes
Track per-field validating counters (increment when a field's async validator starts, decrement when it resolves). Publish a boolean validating to consumers (true if counter > 0). This prevents early flipping to false while later validations are still running.
Record per-field "last async run id" along with a global run id to determine relevance of a resolved async result and avoid suppressing applicable results.
Merge field-level async results into a per-run temporary store and merge into formState.errors at resolution-time for relevant fields (only apply per-field results that have not been superseded).
Schedule notifications in a way that avoids re-entrancy into validation (use the configured callback scheduler for async notifications) and ensure we don't double-notify subscribers while still ensuring missing notifications are delivered.
Summary
When multiple fields have independent async field-level validators (
validateFields: []), making rapid changes to different fields can cause some fields' validation results to be suppressed (not applied to their FieldState) — issue reproducible and reported by When using async field-level validation withvalidateFieldsset to[], making changes to multiple fields causes some errors to be suppressed. #492.When a field runs multiple overlapping async validations (user edits while a previous validation is still pending), the
validatingflag on that FieldState may be reset tofalsewhen an earlier promise resolves while a later validation is still running — reported by When using async field-level validation, thevalidatingFieldStategets reset tofalseeven if validation is ongoing #491.Why this is high priority
Reproduction (POC)
I added a reproducible POC script at
poc/async-validation-poc.ts. Two scenarios are provided:scenario1_suppressedErrorsdemonstrates that two fields withvalidateFields: []and independent async validators may end with one field's error missing.scenario2_validatingFlagdemonstrates overlapping validations on the same field wherevalidatingcan be inconsistent.Run the POC locally (recommended to use
npx ts-node):validatingshould remaintrueuntil all outstanding validations for that field have finished, and only then flip tofalse.Observed behavior
validatingtoggles too early in some workflows (flips to false while newer validations still in-flight), or subscriber notifications are missed or duplicated due to race conditions.Root cause analysis (summary)
nextAsyncValidationKeyfor global async runs and used Promise resolution order + comparison with that key to decide whether to ignore a resolved result.Suggested fixes
validatingto consumers (true if counter > 0). This prevents early flipping to false while later validations are still running.formState.errorsat resolution-time for relevant fields (only apply per-field results that have not been superseded).Files added as part of the investigation
src/FinalForm.validating.test.tscontains tests reproducing both reported problems (When using async field-level validation, thevalidatingFieldStategets reset tofalseeven if validation is ongoing #491, When using async field-level validation withvalidateFieldsset to[], making changes to multiple fields causes some errors to be suppressed. #492).