[v2][vue] Field components registered with `createFormHook` don't rerender on field state changes, and lose their field API after `form.reset()`
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 3/5
- Geschätzter Aufwand
- 1-2 Tage
- Anfängerfreundlichkeit
- 35/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- typescript
- Bereich
- frontend
Rechercherichtung
Beginne mit src/AppForm/fieldComponentHelpers.lib.ts und src/VueForm/Components.lib.ts und untersuche anschließend die in PR #2392 referenzierten Regressionstests. Reproduziere den Fehler mit dem Vue-Formular-Test-Setup und überprüfe, dass injizierte Feldkomponenten nach Änderungen an der Validierung aktualisiert werden und das Formular nach form.reset() weiterhin aktualisieren.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Describe the bug
In @tanstack/[email protected], field components registered through createFormHook with getFormHookHelpers().fieldComponent.strict() (the pattern used in examples/vue/large-form) only render once:
- They don't rerender when the field state changes. After a change that produces a validation error, the form state is correct (
form.getFieldMeta('name').errorscontains the error andmeta.isInvalidistrue), but the injected component keeps showing its first render: no error message andaria-invalid="false". Rendering the same field directly inside the<form.Field>slot updates as expected. - They lose their field API after
form.reset(). After a reset, typing into the injected component no longer updates the form value.
Root cause
wrapField(src/AppForm/fieldComponentHelpers.lib.ts) injects the field API and passes the same object as a prop on every render. The parentFieldrerenders its slot because of its own subscription, but Vue skips updating the injected component because its props are unchanged (same object reference), and the child's reads of the field API getters are not tracked by Vue.createFieldComponent(src/VueForm/Components.lib.ts) callsprovide(fieldContext, fieldApi.value)once during setup.useFieldreplacesfieldApi.valuewhenresetVersionchanges, so afterform.reset()the injected component keeps the old instance. The comment above that line ("Field APIs are stable for a mounted name… the parent subscription handles state-driven renders") doesn't hold for injected components.
Your minimal, reproducible example
Regression tests are included in PR #2392. Minimal Vue SFC repro:
<!-- TextField.vue — same as examples/vue/large-form/src/components/TextField.vue -->
<script setup lang="ts">
import type { FieldWithValue } from '@tanstack/vue-form'
defineProps<{ field: FieldWithValue<string>; label: string }>()
</script>
<template>
<label :for="field.name">{{ label }}</label>
<input
:id="field.name"
:value="field.value"
:aria-invalid="field.meta.isInvalid"
@input="field.handleChange(($event.target as HTMLInputElement).value)"
/>
<p v-for="error in field.errors" :key="error.message">{{ error.message }}</p>
</template>
<!-- Form.vue -->
<script setup lang="ts">
import { createFormHook, getFormHookHelpers } from '@tanstack/vue-form'
import TextField from './TextField.vue'
const { fieldComponent } = getFormHookHelpers()
const { useAppForm } = createFormHook({
fieldComponents: { TextField: fieldComponent.strict(TextField, 'field') },
formComponents: {},
})
const form = useAppForm({
defaultValues: { name: '' },
validators: [
{
triggers: ['change'],
run: ({ value, createErrorMap }) => {
const errors = createErrorMap()
if (value.name.length < 2) errors.fields.name = 'At least 2 characters'
return errors
},
},
],
})
</script>
<template>
<form.Field name="name" v-slot="{ field }">
<field.TextField label="Name" />
</form.Field>
<button type="button" @click="form.reset()">Reset</button>
</template>
Steps to reproduce
- Render
Form.vue. - Type one character into the input.
form.getFieldMeta('name').errorscontains "At least 2 characters", butTextFieldshows no error andaria-invalidstays"false".- Click Reset, then type again:
form.state.values.namedoesn't change.
Expected behavior
Injected field components rerender when their field state changes and keep working after form.reset(), the same as markup rendered directly inside the <form.Field> slot.
How often does this bug happen?
Every time
Platform
- macOS, Node 24
- Reproduced in Vitest with both happy-dom and jsdom (the upstream
packages/vue-formtest setup) - Vue 3.6.0-rc.8
TanStack Form adapter
vue-form
TanStack Form version
2.0.0-alpha.2 (also reproduced on the alpha branch at 6e5b809)
TypeScript version
6.0.3
Additional context
I opened PR #2392 with a fix and two regression tests. Both tests fail before the change and pass after it.
- Vorherrschende Sprache
- TypeScript
- Sterne
- 6.8k
- Forks
- 705
- Ø Merge
- 1 T. 7 Std.
- Gemergte PRs (30 T.)
- 13
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus TanStack/form
-
[v2] FieldWithValue<T> erases field.name to any instead of stringEvtl. vergeben @Priyeshshrivastav hat das vor 1 Tag übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 79/100
TanStack/form#2465 · 3 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
`isDefaultValue` is wrong when a field-level `defaultValue` is `null`Evtl. vergeben @fenet-as hat das vor 24 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
TanStack/form#2427 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Async validator doesn't re-trigger `isValidating` after first failed validationEvtl. vergeben @drewsephski hat das vor 28 Tagen übernommen. Offen
Schwierigkeit 2/5 1-2 Tage Anfängerfreundlichkeit 72/100
Maintainer antworten meist innerhalb von 1 Tag
-
[v2 alpha] devtools are always light modeEvtl. vergeben @dikshit-n hat das vor 62 Tagen übernommen. Offenarea: runtime scope: devtools type: bug v2
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
Maintainer antworten meist innerhalb von 1 Tag
-
FormGroupApi.getRelatedFields and FieldApi.validate() use unguarded startsWith for group membership, matching unrelated fields with a shared name prefixEvtl. vergeben @HDPark95 hat das vor 81 Tagen übernommen. Offenarea: runtime scope: core type: bug v1
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
TanStack/form#2337 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
Ähnliche Issues
-
unwrapDatabaseError masks real DB errors with TypeError: Cannot convert object to primitive valueOffen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
NEARBuilders/everything-dev#798 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
vercel-labs/durable-harness#53 ·
Maintainer antworten meist innerhalb von 1 Tag
-
[Bug]: Redesign tab bars ignore clicks on Linux: hidden chat layers keep window drag regionsEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
Maintainer antworten meist innerhalb von 1 Tag
-
experiência: a doc manda subir para o Micro do Supabase, mas não diz que exige plano pago nem quanto custaEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offenarea/docs area/ui enhancement
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 88/100
melgarafael/DeskcommCRM#4098 ·
Maintainer antworten meist innerhalb von 1 Tag
-
dependencies platform: ios
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 78/100
software-mansion/react-native-executorch#1617 ·
Maintainer antworten meist innerhalb von 1 Tag