X Tutup
Skip to content

fix: preserve dirty/touched meta on late mount (#5072)#5145

Open
logaretm wants to merge 1 commit intomainfrom
fix/5072-dirty-meta-lost-on-mount
Open

fix: preserve dirty/touched meta on late mount (#5072)#5145
logaretm wants to merge 1 commit intomainfrom
fix/5072-dirty-meta-lost-on-mount

Conversation

@logaretm
Copy link
Owner

@logaretm logaretm commented Mar 4, 2026

Summary

  • Fixes Field "dirty" meta lost after child component mounted for the first time #5072: Field "dirty" meta lost after child component mounted for the first time
  • When setFieldValue changes a field's value before the field's host component mounts, the dirty and touched meta flags are now preserved when the component finally mounts and useField runs
  • Two changes:
    • useFieldState.ts: Skip calling stageInitialValue when the form already has an initial value for the path and no explicit modelValue was provided. This prevents overwriting the original initial value with the current (dirty) value.
    • useForm.ts: In createPathState, preserve the touched flag from any pre-existing path state (e.g., one created by setFieldValue) when creating a new path state for the same path.

Test plan

  • Added test: preserves dirty meta when field component mounts after programmatic value changes
  • Added test: preserves touched meta when field component mounts after programmatic changes
  • All existing 357 tests pass (3 pre-existing import failures in i18n/rules/validate packages are unrelated)

🤖 Generated with Claude Code

When a field component mounts after its value was changed
programmatically (e.g., via setFieldValue), the dirty and touched
meta flags are now preserved instead of being reset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 4, 2026 07:04
@changeset-bot
Copy link

changeset-bot bot commented Mar 4, 2026

🦋 Changeset detected

Latest commit: 7344bc8

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify
Copy link

netlify bot commented Mar 4, 2026

Deploy Preview for vee-validate-docs canceled.

Name Link
🔨 Latest commit 7344bc8
🔍 Latest deploy log https://app.netlify.com/projects/vee-validate-docs/deploys/69a7d97649f6700008ff44b7

@netlify
Copy link

netlify bot commented Mar 4, 2026

Deploy Preview for vee-validate-v5 ready!

Name Link
🔨 Latest commit 7344bc8
🔍 Latest deploy log https://app.netlify.com/projects/vee-validate-v5/deploys/69a7d9763f41a20008d1984c
😎 Deploy Preview https://deploy-preview-5145--vee-validate-v5.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes vee-validate late-mount behavior where programmatic updates (e.g., setFieldValue, setFieldTouched) made before a field component mounts could be lost/overwritten when useField initializes, addressing #5072.

Changes:

  • Adjusts field initialization to avoid restaging an initial value when the form already has one, preserving “dirty” on late mount.
  • Preserves touched when createPathState is called for a path that already had state created pre-mount.
  • Adds regression tests covering dirty/touched preservation on late mount and includes a changeset entry.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
packages/vee-validate/src/useFieldState.ts Skips stageInitialValue in some cases to avoid overwriting initial values and resetting dirty state.
packages/vee-validate/src/useForm.ts Preserves touched when creating path state after a pre-existing state was created programmatically.
packages/vee-validate/tests/useForm.spec.ts Adds regression tests for dirty/touched preservation when fields mount after programmatic updates.
.changeset/fix-5072-dirty-meta-lost.md Adds a patch-level changeset entry for the fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +167 to +170
const existingInitial = getFromPath(form.initialValues.value, unref(path));
if (existingInitial === undefined || modelValue !== undefined) {
form.stageInitialValue(unref(path), currentValue, true);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

existingInitial === undefined can't distinguish between “path missing from initialValues” vs “path present with an explicit initial value of undefined”. In that latter case this will still call stageInitialValue and can overwrite the intended initial state, potentially reintroducing the dirty-meta regression for undefined initial values. Consider checking path presence explicitly (e.g., use getFromPath(..., IS_ABSENT) and compare against the sentinel, plus a dedicated presence check for [nonNested] paths) rather than comparing the resolved value to undefined.

Copilot uses AI. Check for mistakes.
Comment on lines +290 to +298
// Preserve touched state from an existing path state that was created
// before this field component mounted (e.g., via setFieldValue) (#5072)
const existingTouched = pathStateExists ? pathStateExists.touched : false;

const id = FIELD_ID_COUNTER++;
const state = reactive({
id,
path,
touched: false,
touched: existingTouched,
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

In the #5072 scenario, setFieldValue() creates a path state before the field mounts, and then useFieldState() calls createPathState() again on mount. For non-checkbox/radio fields, createPathState() always creates/pushes a new reactive state even when pathStateExists is truthy, leaving the pre-mount state orphaned in pathStates (can’t be removed because it has a different id). Copying touched mitigates one symptom, but the duplicate/orphan state is still a correctness and memory concern and can also drop other programmatically-set meta (e.g. errors/validated). Consider reusing/updating the existing state when one already exists (similar to the checkbox/radio branch), or explicitly removing/replacing the old state before pushing the new one.

Copilot uses AI. Check for mistakes.
"vee-validate": patch
---

Preserve dirty meta when field component mounts after programmatic changes (#5072)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

The changeset note mentions preserving dirty meta, but this PR also changes touched preservation on late mount. Consider updating the changeset description to reflect both behaviors so the release note matches the shipped fix.

Suggested change
Preserve dirty meta when field component mounts after programmatic changes (#5072)
Preserve dirty and touched meta when field component mounts after programmatic changes (#5072)

Copilot uses AI. Check for mistakes.
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.

Field "dirty" meta lost after child component mounted for the first time

2 participants

X Tutup