From 42a31d56da1bc1050563fdc5088db15a71aae291 Mon Sep 17 00:00:00 2001 From: balhyo-younjisang Date: Tue, 3 Mar 2026 20:10:52 +0900 Subject: [PATCH 1/3] fix(solid-form): fix props reactivity in withForm and withFieldGroup - Replace object spread with mergeProps() to preserve reactive getters - Call render via createComponent() to maintain SolidJS reactive context - Add regression tests for withForm and withFieldGroup reactivity --- packages/solid-form/src/createFormHook.tsx | 12 +- .../solid-form/tests/createFormHook.test.tsx | 126 +++++++++++++++++- 2 files changed, 134 insertions(+), 4 deletions(-) diff --git a/packages/solid-form/src/createFormHook.tsx b/packages/solid-form/src/createFormHook.tsx index c94b365c8..2b76a2286 100644 --- a/packages/solid-form/src/createFormHook.tsx +++ b/packages/solid-form/src/createFormHook.tsx @@ -1,6 +1,7 @@ import { createComponent, createContext, + mergeProps, splitProps, useContext, } from 'solid-js' @@ -468,7 +469,10 @@ export function createFormHook< UnwrapOrAny, UnwrapOrAny >['render'] { - return (innerProps) => render({ ...props, ...innerProps }) + return (innerProps) => createComponent( + render as Component, + mergeProps(props ?? {}, innerProps) + ); } function withFieldGroup< @@ -553,8 +557,10 @@ export function createFormHook< formComponents: opts.formComponents, } const fieldGroupApi = createFieldGroup(() => fieldGroupProps) - - return render({ ...props, ...innerProps, group: fieldGroupApi as any }) + return createComponent( + render as Component, + mergeProps(props ?? {}, innerProps, { group: fieldGroupApi as any }) + ) } } diff --git a/packages/solid-form/tests/createFormHook.test.tsx b/packages/solid-form/tests/createFormHook.test.tsx index d6eea7b98..b329e71d3 100644 --- a/packages/solid-form/tests/createFormHook.test.tsx +++ b/packages/solid-form/tests/createFormHook.test.tsx @@ -1,7 +1,8 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { render } from '@solidjs/testing-library' import { formOptions } from '@tanstack/form-core' import userEvent from '@testing-library/user-event' +import { createEffect, createSignal } from 'solid-js' import { createFormHook, createFormHookContexts, useStore } from '../src' const user = userEvent.setup() @@ -535,6 +536,129 @@ describe('createFormHook', () => { render(() => ) }) + it('should keep props reactive in JSX when passed to withForm component', async () => { + const formOpts = formOptions({ defaultValues: { name: '' } }) + + const StatusForm = withForm({ + ...formOpts, + props: { + status: 'idle' as 'idle' | 'loading', + count: 0, + }, + render: (props) => ( +
+ {props.status} + {props.count} +
+ ), + }) + + const Parent = () => { + const form = useAppForm(() => formOpts) + const [status, setStatus] = createSignal<'idle' | 'loading'>('idle') + const [count, setCount] = createSignal(0) + return ( +
+ + + +
+ ) + } + + const { getByTestId } = render(() => ) + + expect(getByTestId('status')).toHaveTextContent('idle') + expect(getByTestId('count')).toHaveTextContent('0') + + await user.click(getByTestId('btn-status')) + expect(getByTestId('status')).toHaveTextContent('loading') + + await user.click(getByTestId('btn-count')) + expect(getByTestId('count')).toHaveTextContent('1') + }) + + it('should re-run createEffect when reactive props change in withForm render', async () => { + const formOpts = formOptions({ defaultValues: { name: '' } }) + const spy = vi.fn() + + const StatusForm = withForm({ + ...formOpts, + props: { status: 'idle' as 'idle' | 'loading' }, + render: (props) => { + createEffect(() => { + spy(props.status) + }) + return
{props.status}
+ }, + }) + + const Parent = () => { + const form = useAppForm(() => formOpts) + const [status, setStatus] = createSignal<'idle' | 'loading'>('idle') + return ( +
+ + +
+ ) + } + + const { getByTestId } = render(() => ) + + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenLastCalledWith('idle') + + await user.click(getByTestId('btn')) + expect(spy).toHaveBeenCalledTimes(2) + expect(spy).toHaveBeenLastCalledWith('loading') + }) + + it('should keep props reactive in withFieldGroup component', async () => { + const formOpts = formOptions({ + defaultValues: { person: { firstName: 'John' } }, + }) + + const PersonGroup = withFieldGroup({ + defaultValues: formOpts.defaultValues.person, + props: { label: 'default' }, + render: (props) => ( +
+ {props.label} +
+ ), + }) + + const Parent = () => { + const form = useAppForm(() => formOpts) + const [label, setLabel] = createSignal('initial') + return ( +
+ + +
+ ) + } + + const { getByTestId } = render(() => ) + + expect(getByTestId('label')).toHaveTextContent('initial') + + await user.click(getByTestId('btn')) + expect(getByTestId('label')).toHaveTextContent('updated') + }) + it('should accept formId and return it', async () => { function Submit() { const form = useFormContext() From 68779f62b95188cf929a4ee96b2211829ebb1d6f Mon Sep 17 00:00:00 2001 From: balhyo-younjisang Date: Tue, 3 Mar 2026 20:10:52 +0900 Subject: [PATCH 2/3] fix(solid-form): fix props reactivity in withForm and withFieldGroup - Replace object spread with mergeProps() to preserve reactive getters - Call render via createComponent() to maintain SolidJS reactive context - Add regression tests for withForm and withFieldGroup reactivity Fixes #2054 --- .changeset/fix-withform-props-reactivity.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/fix-withform-props-reactivity.md diff --git a/.changeset/fix-withform-props-reactivity.md b/.changeset/fix-withform-props-reactivity.md new file mode 100644 index 000000000..7f958842b --- /dev/null +++ b/.changeset/fix-withform-props-reactivity.md @@ -0,0 +1,7 @@ +--- +"@tanstack/solid-form": patch +--- + +Fix props passed to `withForm` and `withFieldGroup` not being reactive. + +Object spread (`{ ...props, ...innerProps }`) was eagerly evaluating SolidJS reactive getters, producing a static snapshot that broke signal tracking. Replaced with `mergeProps()` to preserve getter descriptors and `createComponent()` to maintain the correct reactive context. From 1b15b8425e806989a0abd483a498b22ac2055a7a Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:21:32 +0000 Subject: [PATCH 3/3] ci: apply automated fixes and generate docs --- .changeset/fix-withform-props-reactivity.md | 2 +- packages/solid-form/src/createFormHook.tsx | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.changeset/fix-withform-props-reactivity.md b/.changeset/fix-withform-props-reactivity.md index 7f958842b..98dcb6574 100644 --- a/.changeset/fix-withform-props-reactivity.md +++ b/.changeset/fix-withform-props-reactivity.md @@ -1,5 +1,5 @@ --- -"@tanstack/solid-form": patch +'@tanstack/solid-form': patch --- Fix props passed to `withForm` and `withFieldGroup` not being reactive. diff --git a/packages/solid-form/src/createFormHook.tsx b/packages/solid-form/src/createFormHook.tsx index 2b76a2286..a86afab24 100644 --- a/packages/solid-form/src/createFormHook.tsx +++ b/packages/solid-form/src/createFormHook.tsx @@ -469,10 +469,11 @@ export function createFormHook< UnwrapOrAny, UnwrapOrAny >['render'] { - return (innerProps) => createComponent( - render as Component, - mergeProps(props ?? {}, innerProps) - ); + return (innerProps) => + createComponent( + render as Component, + mergeProps(props ?? {}, innerProps), + ) } function withFieldGroup< @@ -559,7 +560,7 @@ export function createFormHook< const fieldGroupApi = createFieldGroup(() => fieldGroupProps) return createComponent( render as Component, - mergeProps(props ?? {}, innerProps, { group: fieldGroupApi as any }) + mergeProps(props ?? {}, innerProps, { group: fieldGroupApi as any }), ) } }