From bf2642f7b93b1602992aa5a6d112ea2c211c185e Mon Sep 17 00:00:00 2001 From: Isaac Hunja Date: Fri, 6 Mar 2026 07:49:53 +0300 Subject: [PATCH 1/3] fix(react-form): default selector to identity in Subscribe component When is rendered without a selector prop, it crashes at runtime because useStore receives undefined as the selector: TypeError: selector is not a function The types already mark selector as optional, so the runtime behavior should match. This adds a default identity function ((state) => state) for the selector parameter in both LocalSubscribe implementations (useForm and useFieldGroup). Fixes #2063 --- packages/react-form/src/useFieldGroup.tsx | 4 ++-- packages/react-form/src/useForm.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-form/src/useFieldGroup.tsx b/packages/react-form/src/useFieldGroup.tsx index 063187c52..a3e05df9f 100644 --- a/packages/react-form/src/useFieldGroup.tsx +++ b/packages/react-form/src/useFieldGroup.tsx @@ -23,11 +23,11 @@ import type { LensFieldComponent } from './useField' function LocalSubscribe({ lens, - selector, + selector = (state) => state, children, }: PropsWithChildren<{ lens: AnyFieldGroupApi - selector: (state: FieldGroupState) => FieldGroupState + selector?: (state: FieldGroupState) => FieldGroupState }>): ReturnType { const data = useStore(lens.store, selector) diff --git a/packages/react-form/src/useForm.tsx b/packages/react-form/src/useForm.tsx index 2d75f0d77..d50651c02 100644 --- a/packages/react-form/src/useForm.tsx +++ b/packages/react-form/src/useForm.tsx @@ -139,11 +139,11 @@ export type ReactFormExtendedApi< function LocalSubscribe({ form, - selector, + selector = (state) => state, children, }: PropsWithChildren<{ form: AnyFormApi - selector: (state: AnyFormState) => AnyFormState + selector?: (state: AnyFormState) => AnyFormState }>): ReturnType { const data = useStore(form.store, selector) From c80ebbc4d51f0f0524aeca63bf93136b82abd2ac Mon Sep 17 00:00:00 2001 From: Isaac Hunja Date: Fri, 6 Mar 2026 11:34:39 +0300 Subject: [PATCH 2/3] test: add coverage for Subscribe default selector --- .../react-form/tests/createFormHook.test.tsx | 59 ++++++++++++++++++- packages/react-form/tests/useForm.test.tsx | 41 +++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/packages/react-form/tests/createFormHook.test.tsx b/packages/react-form/tests/createFormHook.test.tsx index 57df138e5..87fe5c978 100644 --- a/packages/react-form/tests/createFormHook.test.tsx +++ b/packages/react-form/tests/createFormHook.test.tsx @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { render } from '@testing-library/react' +import { render, waitFor } from '@testing-library/react' import { formOptions } from '@tanstack/form-core' import userEvent from '@testing-library/user-event' import { createFormHook, createFormHookContexts, useStore } from '../src' @@ -699,4 +699,61 @@ describe('createFormHook', () => { const button = getByText('Testing') expect(button).toBeInTheDocument() }) + + it('should render FieldGroup Subscribe without selector (default identity)', async () => { + const formOpts = formOptions({ + defaultValues: { + person: { + firstName: 'FirstName', + lastName: 'LastName', + }, + }, + }) + + const ChildFormAsField = withFieldGroup({ + defaultValues: formOpts.defaultValues.person, + render: ({ group }) => { + return ( +
+ ( + + )} + /> + ( + {state.values.lastName} + )} + /> +
+ ) + }, + }) + + const Parent = () => { + const form = useAppForm({ + ...formOpts, + }) + return + } + + const { getByTestId } = render() + const input = getByTestId('lastName') + const stateLastName = getByTestId('state-lastName') + + expect(stateLastName).toHaveTextContent('LastName') + + await user.clear(input) + await user.type(input, 'Updated') + await waitFor(() => expect(stateLastName).toHaveTextContent('Updated')) + }) }) diff --git a/packages/react-form/tests/useForm.test.tsx b/packages/react-form/tests/useForm.test.tsx index ce6733fcb..f93c07308 100644 --- a/packages/react-form/tests/useForm.test.tsx +++ b/packages/react-form/tests/useForm.test.tsx @@ -1075,4 +1075,45 @@ describe('useForm', () => { await user.click(getByText('Rerender')) await findByText('Another') }) + + it('should render Subscribe without selector (default identity)', async () => { + function Comp() { + const form = useForm({ + defaultValues: { + name: 'test', + }, + }) + + return ( + <> + ( + field.handleChange(e.target.value)} + /> + )} + /> + + ( + {state.values.name} + )} + /> + + ) + } + + const { getByTestId } = render() + const input = getByTestId('input') + const stateValue = getByTestId('state-value') + + expect(stateValue).toHaveTextContent('test') + + await user.clear(input) + await user.type(input, 'updated') + await waitFor(() => expect(stateValue).toHaveTextContent('updated')) + }) }) From e2e3aa7a6b4aeab5267895389e6f69d188f30c0c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 08:54:35 +0000 Subject: [PATCH 3/3] ci: apply automated fixes and generate docs --- packages/react-form/tests/createFormHook.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-form/tests/createFormHook.test.tsx b/packages/react-form/tests/createFormHook.test.tsx index 87fe5c978..273746d0b 100644 --- a/packages/react-form/tests/createFormHook.test.tsx +++ b/packages/react-form/tests/createFormHook.test.tsx @@ -731,7 +731,9 @@ describe('createFormHook', () => { /> ( - {state.values.lastName} + + {state.values.lastName} + )} />