fix(createFormControl): skip setValid() during batch array updates #13140
+262
−2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Proposed Changes
Problem:
#13129
When using useFieldArray.replace to update data, the performance becomes very slow and can cause the browser to freeze.
This happens because the entire form validation resolver is triggered for every single replaced field.
Root cause flow:
replaceis called (it updates_formValues, sets fields, and calls_setFieldArray).react-hook-form/src/useFieldArray.ts
Lines 306 to 323 in fb6423f
React re-renders because the fields have changed; during rendering, the JSX executes
register(name)for each input.In every single field new registration,
updateValidAndValueis executed, which internally callssetValid.react-hook-form/src/logic/createFormControl.ts
Lines 1127 to 1136 in fb6423f
react-hook-form/src/logic/createFormControl.ts
Line 323 in fb6423f
setValidalways triggers_runSchema()without any arguments, which means it validates the entire form.This is the root cause of the performance issue, the whole form is being validated for every newly registered field.
react-hook-form/src/logic/createFormControl.ts
Lines 190 to 192 in fb6423f
Please refer to the following CodeSandbox example, which is based on the one from issue #13129.
It now logs how many times the resolver is called.
Open the console, click
add data, and wait for the execution, this demonstrates that the form validation resolver runs once per field, confirming the issue.https://codesandbox.io/p/devbox/reslover-isvalid-issue-forked-thzpcv
Fix:
When
replacecalls_setFieldArray, it raises a flag by setting_state.actiontotrue, and only resets it tofalseinside auseEffect, meaning this happens after the changes are committed.react-hook-form/src/logic/createFormControl.ts
Line 234 in fb6423f
react-hook-form/src/useFieldArray.ts
Line 326 in fb6423f
The
registerexecutions triggered by the re-render occur before the commit, so if we add an_state.actioncheck inside the_setValid()call withinupdateValidAndValue, we can safely skip the validator call during field updates.This doesn’t mean the validator is skipped entirely, at the end of the
useEffect,_setValid()is called again, ensuring the form is always validated with the resolver once the update is complete.react-hook-form/src/useFieldArray.ts
Line 412 in fb6423f
Fixes #13129
Performance Benchmark
The table below compares the execution time before and after the optimization when replacing a batch of fields.
Type of change
Checklist: