Skip to content

Fix #1093: useField read current value on the render#1094

Open
romancavry wants to merge 2 commits into
final-form:mainfrom
romancavry:fix/1093/use-field-value
Open

Fix #1093: useField read current value on the render#1094
romancavry wants to merge 2 commits into
final-form:mainfrom
romancavry:fix/1093/use-field-value

Conversation

@romancavry

@romancavry romancavry commented Jun 30, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • Bug Fixes
    • Improved initial field state handling so newly added fields (including array paths) display the current value immediately.
    • Corrected dirty/pristine tracking, including more consistent behavior for multi-select fields, so state accurately reflects whether values changed.
  • Tests
    • Added a test to confirm that newly created array fields render the expected value on first render and that state flags (e.g., dirty/initial) are set correctly.

@romancavry romancavry requested a review from erikras as a code owner June 30, 2026 13:58
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

useField now derives its initial FieldState from formState.values and formState.initialValues instead of hardcoding defaults. pristine is computed via isEqual, dirty is set to !pristine, and value reflects the current form state on first render. A new test covers the dynamic array field case.

Changes

useField initial state derivation

Layer / File(s) Summary
Initial FieldState value/pristine/dirty computation
src/useField.ts
Reads current value from formState.values via getIn, computes initialStateValue from formState.initialValues or field initialValue, normalizes select-with-multiple for both value and initialStateValue, derives pristine via isEqual, and wires dirty: !pristine, pristine, and value to computed results instead of hardcoded false/true/initialStateValue.
New test: array field first render state
src/useField.test.js
Adds a test that dynamically adds an array item via form.change("items", ...), then asserts useField("items[0].name") reports value: "Apple", meta.initial: undefined, and meta.dirty: true on first render.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • final-form/react-final-form#1060: Updates useField.ts to change how initial value is derived for nested/array paths on first render, directly related to this PR's approach.

Suggested reviewers

  • erikras

Poem

🐇 A field woke up with a brand-new glow,
Its value was there, and now we know.
Pristine or dirty? isEqual decides,
With apples in arrays where the bunny abides.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: making useField read the current value during render.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@romancavry

Copy link
Copy Markdown
Author

Fixes #1094

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/useField.ts`:
- Around line 129-144: The select-multiple normalization in useField is creating
separate empty arrays for value and initialStateValue when one side is
undefined, which makes the default isEqual check in pristine treat equivalent
empty selections as dirty. Update the empty-array handling inside the multiple
branch so that when one side is already [], the undefined side reuses that same
array instance instead of allocating a new one. Keep the fix localized to the
value/initialStateValue normalization logic in useField.
- Around line 124-128: The value selection in useField is treating undefined
from formState.values as “missing,” which causes a cleared field to fall back to
the initial value. Update the initial value resolution in useField so that, when
the form has an initial value for the same path, the current form state is
always preferred even if it is undefined; only use the field-level initialValue
when the field value is truly absent before registration. Keep the fix localized
to the initialStateValue/value logic and preserve the existing behavior for
untouched fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6752aecd-953d-46c2-99b2-a5c6f532f24d

📥 Commits

Reviewing files that changed from the base of the PR and between e09c3cf and 64d609e.

📒 Files selected for processing (2)
  • src/useField.test.js
  • src/useField.ts

Comment thread src/useField.ts
Comment thread src/useField.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/useField.ts (1)

127-132: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mirror this precedence in the pre-registration effect.

Lines 127-132 preserve a cleared current value on first render, but Lines 198-203 can still overwrite it with formInitialValue before registration completes when existingFieldState is missing. Use the same current-value precedence there so a cleared field is not reset during mount.

🐛 Proposed fix
     if (!existingFieldState) {
       const formState = form.getState();
       const formInitialValue = formState.initialValues ? getIn(formState.initialValues, name) : undefined;
-      const valueToSet = formInitialValue !== undefined ? formInitialValue : initialValue;
+      const formValue = formState.values
+        ? getIn(formState.values, name)
+        : undefined;
+      const valueToSet =
+        formInitialValue !== undefined
+          ? formValue
+          : formValue !== undefined
+            ? formValue
+            : initialValue;
       if (valueToSet !== undefined) {
         form.change(name as keyof FormValues, valueToSet);
       }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/useField.ts` around lines 127 - 132, The pre-registration effect in
useField is not using the same value precedence as the initial value resolution,
so a cleared current value can be overwritten by formInitialValue before
registration completes. Update the effect that runs before registration to
follow the same precedence as the value logic around formInitialValue,
formValue, and initialStateValue, and ensure existingFieldState missing does not
cause a reset when the field already has a cleared current value. Keep the fix
localized to useField so the mount-time path and the initial value path stay
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/useField.ts`:
- Around line 127-132: The pre-registration effect in useField is not using the
same value precedence as the initial value resolution, so a cleared current
value can be overwritten by formInitialValue before registration completes.
Update the effect that runs before registration to follow the same precedence as
the value logic around formInitialValue, formValue, and initialStateValue, and
ensure existingFieldState missing does not cause a reset when the field already
has a cleared current value. Keep the fix localized to useField so the
mount-time path and the initial value path stay consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 12e28802-8d05-4faf-af3d-4a769172cac8

📥 Commits

Reviewing files that changed from the base of the PR and between 64d609e and fc62ed7.

📒 Files selected for processing (1)
  • src/useField.ts

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.

1 participant