[v2][vue] Field components registered with `createFormHook` don't rerender on field state changes, and lose their field API after `form.reset()`
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 3/5
- Tempo estimado
- 1-2 dias
- Facilidade para iniciantes
- 35/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- typescript
- Domínio
- frontend
Direção de pesquisa
Comece por src/AppForm/fieldComponentHelpers.lib.ts e src/VueForm/Components.lib.ts e, em seguida, inspecione os testes de regressão referenciados em PR #2392. Reproduza a falha com a configuração de testes do formulário Vue e verifique se os componentes de campo injetados são atualizados após alterações na validação e se continuam atualizando o formulário após form.reset().
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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.
- Linguagem predominante
- TypeScript
- Estrelas
- 6.8k
- Forks
- 705
- Merge médio
- 1d 7h
- PRs com merge (30d)
- 13
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de TanStack/form
-
[v2] FieldWithValue<T> erases field.name to any instead of stringTalvez já em andamento @Priyeshshrivastav assumiu há 1 dia. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 79/100
TanStack/form#2465 · 3 comentários ·
Mantenedores costumam responder em até 1 dia
-
`isDefaultValue` is wrong when a field-level `defaultValue` is `null`Talvez já em andamento @fenet-as assumiu há 24 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
TanStack/form#2427 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Async validator doesn't re-trigger `isValidating` after first failed validationTalvez já em andamento @drewsephski assumiu há 28 dias. Aberta
Dificuldade 2/5 1-2 dias Facilidade para iniciantes 72/100
Mantenedores costumam responder em até 1 dia
-
[v2 alpha] devtools are always light modeTalvez já em andamento @dikshit-n assumiu há 62 dias. Abertaarea: runtime scope: devtools type: bug v2
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 65/100
Mantenedores costumam responder em até 1 dia
-
FormGroupApi.getRelatedFields and FieldApi.validate() use unguarded startsWith for group membership, matching unrelated fields with a shared name prefixTalvez já em andamento @HDPark95 assumiu há 81 dias. Abertaarea: runtime scope: core type: bug v1
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
TanStack/form#2337 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
Todas as issues de TanStack/form
Issues semelhantes
-
test-reliability
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Azure/azure-sdk-for-js#40672 · 1 reação ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 74/100
HarperFast/studio#2067 ·
Mantenedores costumam responder em até 2 dias
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 65/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 1 dia
-
documentation
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
liberation-data/drivine#639 · 2 comentários ·
Mantenedores costumam responder em até 1 dia