diff --git a/.changeset/vue-form-field-slot-state.md b/.changeset/vue-form-field-slot-state.md new file mode 100644 index 000000000..9b2c109f0 --- /dev/null +++ b/.changeset/vue-form-field-slot-state.md @@ -0,0 +1,5 @@ +--- +'@tanstack/vue-form': patch +--- + +Fix `Field` scoped slot `state` reactivity after field value and meta updates. diff --git a/packages/vue-form/src/useField.tsx b/packages/vue-form/src/useField.tsx index e51d79681..599154839 100644 --- a/packages/vue-form/src/useField.tsx +++ b/packages/vue-form/src/useField.tsx @@ -375,7 +375,12 @@ export function useField< }, ) - return { api: extendedFieldApi.value, state: fieldState.value } as const + return { + api: extendedFieldApi.value, + get state() { + return fieldState.value + }, + } as const } export type FieldComponentProps< diff --git a/packages/vue-form/tests/useField.test.tsx b/packages/vue-form/tests/useField.test.tsx index f01707cc3..90bfffb90 100644 --- a/packages/vue-form/tests/useField.test.tsx +++ b/packages/vue-form/tests/useField.test.tsx @@ -225,6 +225,84 @@ describe('useField', () => { expect(getByText(error)).toBeInTheDocument() }) + it('should keep field slot state reactive', async () => { + type Person = { + firstName: string + } + + const serverError = 'First name is already taken' + + const Comp = defineComponent(() => { + const form = useForm({ + defaultValues: { + firstName: '', + } as Person, + }) + + function setServerError() { + form.setFieldMeta('firstName', (meta) => ({ + ...meta, + errorMap: { + ...meta.errorMap, + onServer: serverError, + }, + errorSourceMap: { + ...meta.errorSourceMap, + onServer: 'form', + }, + })) + } + + return () => ( +
{state.value}
+{state.meta.errorMap.onServer}
+