Add useFormData hook for full-form value reactivity in apps#87
Conversation
Adds a new `hook/composable/primitive/rune` across all six framework packages that returns a reactive snapshot of the entire form input. Wraps the existing `getInput(form)` in each framework's reactive primitive so a JSON preview of the form data updates on every keystroke. Useful for live previews, debug panels, and conditional rendering based on multiple fields at once.
Each framework returns the type that matches its existing `useField(...).input` convention:
- React: `PartialValues<v.InferInput<TSchema>>` (plain value, hook re-runs on change via useSignals)
- Preact: `ReadonlySignal<PartialValues<...>>` (useComputed)
- Qwik: `ReadonlySignal<PartialValues<...>>` (`useComputed$`, named `useFormData$`)
- Vue: `ComputedRef<PartialValues<...>>` (computed, accepts `MaybeRefOrGetter`)
- Solid: `Accessor<PartialValues<...>>` (`createMemo`, accepts `MaybeGetter`)
- Svelte: `FormDataStore { readonly current: PartialValues<...> }` (getter-backed `$derived`)
Adds documentation pages and menu entries for each framework, plus a new `FormDataStore` type doc page for Svelte. Updates the Solid and React login playgrounds with a JSON preview component that exercises the hook end-to-end.
Minor bump per package (new public API, semver). Prepends a CHANGELOG entry to each package describing the new hook. - @formisch/react: 0.4.5 → 0.5.0 - @formisch/preact: 0.9.5 → 0.10.0 - @formisch/qwik: 0.10.0 → 0.11.0 (useFormData$) - @formisch/solid: 0.9.5 → 0.10.0 - @formisch/svelte: 0.7.5 → 0.8.0 - @formisch/vue: 0.7.5 → 0.8.0
The framework-agnostic packages/core/src/framework/index.ts didn't declare Listener or setListener, but packages/core/src/framework/index.react.ts did. The build's `export *` re-export forwards only names visible on the base module's analysis, so setListener was silently dropped from the React core dist. This broke @formisch/react's useSignals hook (which imports setListener from @formisch/core/react) and any downstream React framework build.
Adds Listener and setListener stub declarations to the base index.ts, matching the existing pattern of "No framework selected" stubs for createId, createSignal, batch, and untrack. The React framework now picks up the real implementation from index.react.ts; the other five frameworks pick up the throwing stub but never call it, so the stub is harmless.
Audit of the other framework-specific files (packages/core/src/framework/index.{preact,qwik,solid,svelte,vue}.ts, packages/core/src/types/*.{fw}.ts, packages/methods/src/**/*.{fw}.ts) found no other missing-export bugs — every other framework variant either redeclares the same names as its base or explicitly re-exports them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Patch release for the React core build fix in the previous commit. - @formisch/core: 0.6.3 → 0.6.4 — adds Listener type and setListener stub to the framework-agnostic framework/index.ts so the bundler includes setListener in the React core dist. - @formisch/react: 0.4.5 → 0.4.6 — pulls in the fixed @formisch/core; the React framework's build (frameworks/react/src/hooks/useSignals/useSignals.ts) was previously failing with "MISSING_EXPORT setListener". The other five framework packages (preact, qwik, solid, svelte, vue) are not bumped here — their builds were not affected by the bug, since none of them import setListener. If you'd prefer to bump them too to follow the existing "bump-frameworks-with-deps" convention used in the v0.4.5 release, happy to do that as a follow-up. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Brings in the React core build fix (Listener + setListener stubs in packages/core/src/framework/index.ts) so the React playground can build and exercise the new useFormData hook. Conflict resolution: - frameworks/react/package.json: kept 0.5.0 from HEAD; the setListener fix's 0.4.6 patch bump is naturally superseded by the useFormData minor bump (semver minor includes patch fixes). - frameworks/react/CHANGELOG.md: rolled the setListener-related core dependency change into the existing v0.5.0 entry as a second bullet, dropped the standalone v0.4.6 entry since the two changes ship together as v0.5.0. The setListener fix will still be sent upstream as its own PR. If that PR lands first, this branch will need a rebase and the consolidated v0.5.0 changelog entry can drop the second bullet (since the fix would already be in main as v0.4.6 by then). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Someone is attempting to deploy a commit to the Open Circle Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thank you for this PR! |
|
Thank you for working on this! I'm not sure if the library should expose such a hook. This is because using such a hook is usually considered poor practice as it forces your component to re-render on every keystroke. It is generally better to only listen to specific inputs. What do you think? |
|
In my case I need to derive data from all form values in realtime. Without Formisch I would still use a similar approach: const form = ref({
exampleA: '',
exampleB: '',
exampleC: ''
});
// Use `derivedExample` in script or display in template.
const derivedExample = computed(() => `${form.value.exampleA}, ${form.value.exampleB}, ${form.value.exampleC}`);// When using Formisch, I always use `getInput()` because I need all form values in realtime anyway.
const formValues = computed(() => getInput(form));
const derivedExample = computed(() => `${formValues.value.exampleA}, ${formValues.value.exampleB}, ${formValues.value.exampleC}`); |
|
Make sense. For now, I recommend using |
| // @__NO_SIDE_EFFECTS__ | ||
| export function useFormData(form: FormStore): unknown { | ||
| useSignals(); | ||
| return getInput(form); |
There was a problem hiding this comment.
The issue with using getInput in React is that it is not reactive to changes and useSignals is not exposed to the user at the moment.
Exposing useSignals will at least give React users a work around.
I get that this can lead to performance issues if used inappropriately however there are legitimate use cases for needing to be reactive to the entire form such as showing live debug info in a UI. Maybe you would be willing to implement something like a Basically taking the same strategy as how React does |
Summary
Adds a new
useFormDatahook (or composable / primitive / rune) across all six framework packages. It wraps the existinggetInput(form)method in each framework's reactive primitive so the full form's input is exposed as a live, schema-shaped object — useful for JSON previews, debug panels, and conditional rendering based on multiple fields.Each framework's return type matches its existing
useField(...).inputconvention so users don't have to learn a new pattern:useFormDataPartialValues<v.InferInput<T>>formData.nameuseFormDataReadonlySignal<PartialValues<…>>formData.value.nameuseFormData$ReadonlySignal<PartialValues<…>>formData.value.nameuseFormDataComputedRef<PartialValues<…>>formData.value.name(script) /{{ formData.name }}(template)useFormDataAccessor<PartialValues<…>>formData().nameuseFormDataFormDataStore { current }formData.current.nameWhat's in this PR
getInput+ the framework's reactive primitive). Plus a folder-level barrel and an entry added to each framework's existing barrel.useFormData(oruseFormData$) MDX pages +properties.tsfiles underwebsite/src/routes/(docs)/. Adds aFormDataStoretype page for Svelte. Each framework's API menu now lists the new entry.loginplaygrounds get aFormDataPreviewcomponent that rendersJSON.stringify(formData, null, 2)underneath the form for end-to-end reactivity verification.@formisch/preact0.9.5 → 0.10.0@formisch/qwik0.10.0 → 0.11.0@formisch/react0.4.5 → 0.5.0@formisch/solid0.9.5 → 0.10.0@formisch/svelte0.7.5 → 0.8.0@formisch/vue0.7.5 → 0.8.0Note on the bundled setListener fix
This branch has the React core build fix from #(setListener PR number) merged in, because without it the React playground can't build and the new hook can't be exercised end-to-end. Affects:
packages/core/src/framework/index.ts— addsListenertype andsetListenerstubpackages/core0.6.3 → 0.6.4If the standalone setListener PR lands first, this branch will need a rebase and the
Change @formisch/core to v0.6.4bullet under@formisch/reactv0.5.0 should drop (since the dep change would belong to the v0.4.6 entry that already shipped). Otherwise the two changes ship together as part of the v0.5.0 react release.Design rationale
getInput(form)already walks the signal tree viagetFieldInput(form[INTERNAL]). When invoked inside a framework's reactive scope, signal reads auto-subscribe and the computation re-runs on any change. The new hook is justframeworkPrimitive(() => getInput(form)).useField(...).input's convention keeps API surface consistent within each framework.$derivedcan't be returned bare from a.svelte.tsfunction and remain reactive at the call site. TheFormDataStore { readonly current }shape matches the existing pattern used bycreateFormin the same package.Test plan
pnpm -C frameworks/{preact,qwik,react,solid,svelte,vue} build— all six build clean.pnpm -C frameworks/{preact,qwik,react,solid,svelte,vue} lint— all six lint clean.pnpm -C playgrounds/solid dev→ http://localhost:3000/login. Type "hello" into the email field. The JSON preview below the form must update on each of the 5 keystrokes (h → he → hel → hell → hello), not just on blur or after the first keystroke. Repeat for password.pnpm -C playgrounds/react dev→ same reactivity check.useFormDatatest scaffolding only added to Solid + React so far; happy to add to the rest if you want spot-checks.pnpm -C website build— new MDX pages and updated menus render without errors. (Note: the website build's SSG step has 30 pre-existing Qwik lexical-scope errors inwebsite/src/routes/playground/*/index.tsxthat are unrelated to this PR — the Vite build itself succeeds.)Summary by cubic
Adds
useFormDataacross React, Preact, Qwik, Vue, Solid, and Svelte to expose the full form input as a live, schema-shaped value for previews and multi-field logic. Includes a small core export fix required for React builds.New Features
useFormDataAPI (useFormData$in Qwik) that wrapsgetInput(form)in each framework’s reactive primitive and updates on every keystroke.useField(...).inputconvention (signals/computed/accessor/value as appropriate).FormDataStoretype page.useFormDatafor end-to-end checks.Dependencies
@formisch/react0.5.0,@formisch/preact0.10.0,@formisch/qwik0.11.0,@formisch/solid0.10.0,@formisch/svelte0.8.0,@formisch/vue0.8.0.@formisch/coreto 0.6.4: addsListenertype andsetListenerstub so React builds succeed.Written for commit 3a0b673. Summary will update on new commits.