From f5b2aee5feddfa393083f82b67d7bbcaf7d52180 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Sun, 9 Mar 2025 22:08:53 +0100 Subject: [PATCH 1/6] feat(core): form listeners --- packages/form-core/src/FieldApi.ts | 68 ++++++++++++ packages/form-core/src/FormApi.ts | 98 ++++++++++++++++ packages/form-core/tests/FormApi.spec.ts | 136 ++++++++++++++++++++++- 3 files changed, 301 insertions(+), 1 deletion(-) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 18940ac2a..02dfca7d4 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1201,6 +1201,11 @@ export class FieldApi< this.triggerOnChangeListener() + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) + this.validate('change') } @@ -1248,6 +1253,15 @@ export class FieldApi< this.form.pushFieldValue(this.name, value as any, opts) this.triggerOnChangeListener() + this.options.listeners?.onChange?.({ + value: this.state.value, + fieldApi: this, + }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) } /** @@ -1261,6 +1275,15 @@ export class FieldApi< this.form.insertFieldValue(this.name, index, value as any, opts) this.triggerOnChangeListener() + this.options.listeners?.onChange?.({ + value: this.state.value, + fieldApi: this, + }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) } /** @@ -1274,6 +1297,15 @@ export class FieldApi< this.form.replaceFieldValue(this.name, index, value as any, opts) this.triggerOnChangeListener() + this.options.listeners?.onChange?.({ + value: this.state.value, + fieldApi: this, + }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) } /** @@ -1283,6 +1315,15 @@ export class FieldApi< this.form.removeFieldValue(this.name, index, opts) this.triggerOnChangeListener() + this.options.listeners?.onChange?.({ + value: this.state.value, + fieldApi: this, + }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) } /** @@ -1292,6 +1333,15 @@ export class FieldApi< this.form.swapFieldValues(this.name, aIndex, bIndex, opts) this.triggerOnChangeListener() + this.options.listeners?.onChange?.({ + value: this.state.value, + fieldApi: this, + }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) } /** @@ -1301,6 +1351,15 @@ export class FieldApi< this.form.moveFieldValues(this.name, aIndex, bIndex, opts) this.triggerOnChangeListener() + this.options.listeners?.onChange?.({ + value: this.state.value, + fieldApi: this, + }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) } /** @@ -1660,6 +1719,15 @@ export class FieldApi< this.validate('blur') this.triggerOnBlurListener() + this.options.listeners?.onBlur?.({ + value: this.state.value, + fieldApi: this, + }) + + this.form.options.listeners?.onBlur?.({ + fieldName: this.name, + formApi: this.form, + }) } /** diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index eaa5544fe..8dbc2ad73 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -233,6 +233,82 @@ export interface FormTransform< deps: unknown[] } +export interface FormListeners< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TField extends DeepKeys, + TSubmitMeta = never, +> { + onChange?: (props: { + fieldName: keyof TFormData + formApi: FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnServer, + TSubmitMeta + > + }) => void + + onBlur?: (props: { + fieldName: TField + formApi: FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnServer, + TSubmitMeta + > + }) => void + + onMount?: (props: { + formApi: FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnServer, + TSubmitMeta + > + }) => void + + onSubmit?: (props: { + formApi: FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnServer, + TSubmitMeta + > + }) => void +} + /** * An object representing the options for a form. */ @@ -299,6 +375,23 @@ export interface FormOptions< */ onSubmitMeta?: TSubmitMeta + /** + * form level listeners + */ + listeners?: FormListeners< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnServer, + DeepKeys, + TSubmitMeta + > + /** * A function to be called when the form is submitted, what should happen once the user submits a valid form returns `any` or a promise `Promise` */ @@ -1049,6 +1142,9 @@ export class FormApi< cleanupFieldMetaDerived() cleanupStoreDerived() } + + this.options.listeners?.onMount?.({ formApi: this }) + const { onMount } = this.options.validators || {} if (!onMount) return cleanup this.validateSync('mount') @@ -1676,6 +1772,8 @@ export class FormApi< ) }) + this.options.listeners?.onSubmit?.({ formApi: this }) + try { // Run the submit code await this.options.onSubmit?.({ diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index e2631c436..57483d540 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -1956,7 +1956,141 @@ describe('form api', () => { expect(form.state.errors).toStrictEqual(['first name is required']) }) - it('should run listener onSubmit', async () => { + it('should run the form listener onSubmit', async () => { + let triggered!: string + const form = new FormApi({ + defaultValues: { + name: 'test', + }, + listeners: { + onSubmit: ({ formApi }) => { + triggered = formApi.state.values.name + }, + }, + }) + + form.mount() + await form.handleSubmit() + + expect(triggered).toStrictEqual('test') + }) + + it('should run the form listener onMount', async () => { + let triggered!: string + const form = new FormApi({ + defaultValues: { + name: 'test', + }, + listeners: { + onMount: ({ formApi }) => { + triggered = formApi.state.values.name + }, + }, + }) + + form.mount() + + expect(triggered).toStrictEqual('test') + }) + + it('should run the form listener onChange', async () => { + let name!: string + + const form = new FormApi({ + defaultValues: { + name: 'test', + age: 0, + }, + listeners: { + onChange: ({ fieldName }) => { + name = fieldName + }, + }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'name', + }) + field.mount() + field.setValue('newTest') + + expect(form.state.values.name).toStrictEqual('newTest') + expect(name).toStrictEqual('name') + }) + + it('should run the form listener onChange when the field array is changed', () => { + let arr!: any + let name!: string + + const form = new FormApi({ + defaultValues: { + items: ['one', 'two'], + age: 0, + }, + listeners: { + onChange: ({ formApi, fieldName }) => { + arr = formApi.state.values[fieldName] + name = fieldName + }, + }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'items', + }) + field.mount() + + field.removeValue(1) + expect(arr).toStrictEqual(['one']) + expect(name).toStrictEqual('items') + + field.replaceValue(0, 'start') + expect(arr).toStrictEqual(['start']) + + field.pushValue('end') + expect(arr).toStrictEqual(['start', 'end']) + + field.insertValue(1, 'middle') + expect(arr).toStrictEqual(['start', 'middle', 'end']) + + field.swapValues(0, 2) + expect(arr).toStrictEqual(['end', 'middle', 'start']) + + field.moveValue(0, 1) + expect(arr).toStrictEqual(['middle', 'end', 'start']) + }) + + it('should run the form listener onBlur', async () => { + let name!: string + + const form = new FormApi({ + defaultValues: { + name: 'test', + age: 0, + }, + listeners: { + onBlur: ({ fieldName }) => { + name = fieldName + }, + }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'name', + }) + field.mount() + field.handleBlur() + + expect(name).toStrictEqual('name') + }) + + it('should run the field listener onSubmit', async () => { const form = new FormApi({ defaultValues: { name: 'test', From c530078ff81b4e06ece2c0fef12c0b463a0e14db Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Wed, 16 Apr 2025 09:47:53 +0200 Subject: [PATCH 2/6] chore: updated to debounced --- packages/form-core/src/FieldApi.ts | 88 +++++++----------------------- 1 file changed, 20 insertions(+), 68 deletions(-) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 02dfca7d4..1919e3169 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1201,11 +1201,6 @@ export class FieldApi< this.triggerOnChangeListener() - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - }) - this.validate('change') } @@ -1253,15 +1248,6 @@ export class FieldApi< this.form.pushFieldValue(this.name, value as any, opts) this.triggerOnChangeListener() - this.options.listeners?.onChange?.({ - value: this.state.value, - fieldApi: this, - }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - }) } /** @@ -1275,15 +1261,6 @@ export class FieldApi< this.form.insertFieldValue(this.name, index, value as any, opts) this.triggerOnChangeListener() - this.options.listeners?.onChange?.({ - value: this.state.value, - fieldApi: this, - }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - }) } /** @@ -1297,15 +1274,6 @@ export class FieldApi< this.form.replaceFieldValue(this.name, index, value as any, opts) this.triggerOnChangeListener() - this.options.listeners?.onChange?.({ - value: this.state.value, - fieldApi: this, - }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - }) } /** @@ -1315,15 +1283,6 @@ export class FieldApi< this.form.removeFieldValue(this.name, index, opts) this.triggerOnChangeListener() - this.options.listeners?.onChange?.({ - value: this.state.value, - fieldApi: this, - }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - }) } /** @@ -1333,15 +1292,6 @@ export class FieldApi< this.form.swapFieldValues(this.name, aIndex, bIndex, opts) this.triggerOnChangeListener() - this.options.listeners?.onChange?.({ - value: this.state.value, - fieldApi: this, - }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - }) } /** @@ -1351,15 +1301,6 @@ export class FieldApi< this.form.moveFieldValues(this.name, aIndex, bIndex, opts) this.triggerOnChangeListener() - this.options.listeners?.onChange?.({ - value: this.state.value, - fieldApi: this, - }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - }) } /** @@ -1719,15 +1660,6 @@ export class FieldApi< this.validate('blur') this.triggerOnBlurListener() - this.options.listeners?.onBlur?.({ - value: this.state.value, - fieldApi: this, - }) - - this.form.options.listeners?.onBlur?.({ - fieldName: this.name, - formApi: this.form, - }) } /** @@ -1783,12 +1715,22 @@ export class FieldApi< value: this.state.value, fieldApi: this, }) + + this.form.options.listeners?.onBlur?.({ + fieldName: this.name, + formApi: this.form, + }) }, debounceMs) } else { this.options.listeners?.onBlur?.({ value: this.state.value, fieldApi: this, }) + + this.form.options.listeners?.onBlur?.({ + fieldName: this.name, + formApi: this.form, + }) } } @@ -1805,12 +1747,22 @@ export class FieldApi< value: this.state.value, fieldApi: this, }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) }, debounceMs) } else { this.options.listeners?.onChange?.({ value: this.state.value, fieldApi: this, }) + + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + }) } } } From f183855b1bed0375fa2d0170d2b81b8960ed9129 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Thu, 17 Apr 2025 18:18:49 +0200 Subject: [PATCH 3/6] chore: updated to include fieldApi --- packages/form-core/src/FieldApi.ts | 4 +++ packages/form-core/src/FormApi.ts | 9 +++++- packages/form-core/tests/FormApi.spec.ts | 35 ++++++++++++++++-------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 1919e3169..b0103eb9f 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1719,6 +1719,7 @@ export class FieldApi< this.form.options.listeners?.onBlur?.({ fieldName: this.name, formApi: this.form, + fieldApi: this, }) }, debounceMs) } else { @@ -1730,6 +1731,7 @@ export class FieldApi< this.form.options.listeners?.onBlur?.({ fieldName: this.name, formApi: this.form, + fieldApi: this, }) } } @@ -1751,6 +1753,7 @@ export class FieldApi< this.form.options.listeners?.onChange?.({ fieldName: this.name as never, formApi: this.form, + fieldApi: this, }) }, debounceMs) } else { @@ -1762,6 +1765,7 @@ export class FieldApi< this.form.options.listeners?.onChange?.({ fieldName: this.name as never, formApi: this.form, + fieldApi: this, }) } } diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 8dbc2ad73..a7b0370a3 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -22,7 +22,12 @@ import type { StandardSchemaV1Issue, TStandardSchemaValidatorValue, } from './standardSchemaValidator' -import type { AnyFieldMeta, AnyFieldMetaBase, FieldApi } from './FieldApi' +import type { + AnyFieldApi, + AnyFieldMeta, + AnyFieldMetaBase, + FieldApi, +} from './FieldApi' import type { FormValidationError, FormValidationErrorMap, @@ -260,6 +265,7 @@ export interface FormListeners< TOnServer, TSubmitMeta > + fieldApi: AnyFieldApi }) => void onBlur?: (props: { @@ -276,6 +282,7 @@ export interface FormListeners< TOnServer, TSubmitMeta > + fieldApi: AnyFieldApi }) => void onMount?: (props: { diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index 57483d540..579decd66 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest' import { z } from 'zod' import { FieldApi, FormApi } from '../src/index' import { sleep } from './utils' +import type { AnyFieldApi, AnyFormApi } from '../src/index' describe('form api', () => { it('should get default form state when default values are passed', () => { @@ -1994,16 +1995,20 @@ describe('form api', () => { }) it('should run the form listener onChange', async () => { - let name!: string + let fieldNameCheck!: string + let fieldApiCheck!: AnyFieldApi + let formApiCheck!: AnyFormApi const form = new FormApi({ defaultValues: { - name: 'test', - age: 0, + name: '', }, listeners: { - onChange: ({ fieldName }) => { - name = fieldName + onChange: ({ fieldName, fieldApi, formApi }) => { + fieldNameCheck = fieldName + fieldApiCheck = fieldApi + + formApiCheck = formApi as any }, }, }) @@ -2016,8 +2021,9 @@ describe('form api', () => { field.mount() field.setValue('newTest') - expect(form.state.values.name).toStrictEqual('newTest') - expect(name).toStrictEqual('name') + expect(fieldNameCheck).toStrictEqual('name') + expect(fieldApiCheck.state.value).toStrictEqual('newTest') + expect(formApiCheck.state.values.name).toStrictEqual('newTest') }) it('should run the form listener onChange when the field array is changed', () => { @@ -2065,7 +2071,9 @@ describe('form api', () => { }) it('should run the form listener onBlur', async () => { - let name!: string + let fieldNameCheck!: string + let fieldApiCheck!: AnyFieldApi + let formApiCheck!: AnyFormApi const form = new FormApi({ defaultValues: { @@ -2073,8 +2081,11 @@ describe('form api', () => { age: 0, }, listeners: { - onBlur: ({ fieldName }) => { - name = fieldName + onBlur: ({ fieldName, fieldApi, formApi }) => { + fieldNameCheck = fieldName + fieldApiCheck = fieldApi + + formApiCheck = formApi as any }, }, }) @@ -2087,7 +2098,9 @@ describe('form api', () => { field.mount() field.handleBlur() - expect(name).toStrictEqual('name') + expect(fieldNameCheck).toStrictEqual('name') + expect(fieldApiCheck.state.value).toStrictEqual('test') + expect(formApiCheck.state.values.name).toStrictEqual('test') }) it('should run the field listener onSubmit', async () => { From 44a8474f8a9a725b14b337da88b0b5aacff7b6a3 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Sun, 20 Apr 2025 21:07:00 +0200 Subject: [PATCH 4/6] chore: clean up and docs --- docs/framework/react/guides/listeners.md | 31 +++++++++++++++++++++ packages/form-core/src/FieldApi.ts | 35 ++++++++---------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/docs/framework/react/guides/listeners.md b/docs/framework/react/guides/listeners.md index 1f68d6a8c..35ad97e28 100644 --- a/docs/framework/react/guides/listeners.md +++ b/docs/framework/react/guides/listeners.md @@ -89,3 +89,34 @@ We enable an easy method for debouncing your listeners by adding a `onChangeDebo )} ``` + +### form listeners + +At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange` and `onBlur` propagated to all the form's children. + +`onMount` and `onSubmit` listeners have to following props: + +- formApi + +`onChange` and `onBlur` listeners have access to formApi, as well as: + +- fieldName +- fieldApi + +```tsx +const form = useForm({ + listeners: { + onMount: ({ formApi }) => { + // custom logging service + loggingService('mount', formApi.state.values) + }, + + onChange: ({ formApi }) => { + // autosave logic + if (formApi.state.isValid) { + formApi.handleSubmit() + } + }, + }, +}) +``` diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index b0103eb9f..8545d5c25 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1704,6 +1704,11 @@ export class FieldApi< private triggerOnBlurListener() { const debounceMs = this.options.listeners?.onBlurDebounceMs + this.form.options.listeners?.onBlur?.({ + fieldName: this.name, + formApi: this.form, + fieldApi: this, + }) if (debounceMs && debounceMs > 0) { if (this.timeoutIds.listeners.blur) { @@ -1715,30 +1720,24 @@ export class FieldApi< value: this.state.value, fieldApi: this, }) - - this.form.options.listeners?.onBlur?.({ - fieldName: this.name, - formApi: this.form, - fieldApi: this, - }) }, debounceMs) } else { this.options.listeners?.onBlur?.({ value: this.state.value, fieldApi: this, }) - - this.form.options.listeners?.onBlur?.({ - fieldName: this.name, - formApi: this.form, - fieldApi: this, - }) } } private triggerOnChangeListener() { const debounceMs = this.options.listeners?.onChangeDebounceMs + this.form.options.listeners?.onChange?.({ + fieldName: this.name as never, + formApi: this.form, + fieldApi: this, + }) + if (debounceMs && debounceMs > 0) { if (this.timeoutIds.listeners.change) { clearTimeout(this.timeoutIds.listeners.change) @@ -1749,24 +1748,12 @@ export class FieldApi< value: this.state.value, fieldApi: this, }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - fieldApi: this, - }) }, debounceMs) } else { this.options.listeners?.onChange?.({ value: this.state.value, fieldApi: this, }) - - this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, - formApi: this.form, - fieldApi: this, - }) } } } From 8cfd8ad2a925899698d931281fceb6fc3ed0391a Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Fri, 25 Apr 2025 14:43:43 +0200 Subject: [PATCH 5/6] chore: pr comments --- docs/framework/react/guides/listeners.md | 23 +++++++++++++---------- packages/form-core/src/FieldApi.ts | 2 -- packages/form-core/src/FormApi.ts | 4 ---- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/docs/framework/react/guides/listeners.md b/docs/framework/react/guides/listeners.md index 35ad97e28..1aff32763 100644 --- a/docs/framework/react/guides/listeners.md +++ b/docs/framework/react/guides/listeners.md @@ -15,10 +15,10 @@ In this example, when the user changes the country, the selected province needs Events that can be "listened" to are: -- onChange -- onBlur -- onMount -- onSubmit +- `onChange` +- `onBlur` +- `onMount` +- `onSubmit` ```tsx function App() { @@ -90,18 +90,18 @@ We enable an easy method for debouncing your listeners by adding a `onChangeDebo ``` -### form listeners +### Form listeners At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange` and `onBlur` propagated to all the form's children. `onMount` and `onSubmit` listeners have to following props: -- formApi +- `formApi` -`onChange` and `onBlur` listeners have access to formApi, as well as: +`onChange` and `onBlur` listeners have access to: -- fieldName -- fieldApi +- `fieldApi` +- `formApi` ```tsx const form = useForm({ @@ -111,11 +111,14 @@ const form = useForm({ loggingService('mount', formApi.state.values) }, - onChange: ({ formApi }) => { + onChange: ({ formApi, fieldApi }) => { // autosave logic if (formApi.state.isValid) { formApi.handleSubmit() } + + // fieldApi represents the field that triggered the event. + console.log(fieldApi.name, fieldApi.state.value) }, }, }) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 8545d5c25..70bcc80a1 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1705,7 +1705,6 @@ export class FieldApi< private triggerOnBlurListener() { const debounceMs = this.options.listeners?.onBlurDebounceMs this.form.options.listeners?.onBlur?.({ - fieldName: this.name, formApi: this.form, fieldApi: this, }) @@ -1733,7 +1732,6 @@ export class FieldApi< const debounceMs = this.options.listeners?.onChangeDebounceMs this.form.options.listeners?.onChange?.({ - fieldName: this.name as never, formApi: this.form, fieldApi: this, }) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index a7b0370a3..5963ae6b8 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -248,11 +248,9 @@ export interface FormListeners< TOnSubmit extends undefined | FormValidateOrFn, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TOnServer extends undefined | FormAsyncValidateOrFn, - TField extends DeepKeys, TSubmitMeta = never, > { onChange?: (props: { - fieldName: keyof TFormData formApi: FormApi< TFormData, TOnMount, @@ -269,7 +267,6 @@ export interface FormListeners< }) => void onBlur?: (props: { - fieldName: TField formApi: FormApi< TFormData, TOnMount, @@ -395,7 +392,6 @@ export interface FormOptions< TOnSubmit, TOnSubmitAsync, TOnServer, - DeepKeys, TSubmitMeta > From 35e8458c81c74d95679a8c30cc3f6ac5aaa09a89 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Fri, 25 Apr 2025 14:50:16 +0200 Subject: [PATCH 6/6] fix: updated tests --- packages/form-core/tests/FormApi.spec.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index 579decd66..b380a63cb 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -1995,7 +1995,6 @@ describe('form api', () => { }) it('should run the form listener onChange', async () => { - let fieldNameCheck!: string let fieldApiCheck!: AnyFieldApi let formApiCheck!: AnyFormApi @@ -2004,8 +2003,7 @@ describe('form api', () => { name: '', }, listeners: { - onChange: ({ fieldName, fieldApi, formApi }) => { - fieldNameCheck = fieldName + onChange: ({ fieldApi, formApi }) => { fieldApiCheck = fieldApi formApiCheck = formApi as any @@ -2021,14 +2019,12 @@ describe('form api', () => { field.mount() field.setValue('newTest') - expect(fieldNameCheck).toStrictEqual('name') expect(fieldApiCheck.state.value).toStrictEqual('newTest') expect(formApiCheck.state.values.name).toStrictEqual('newTest') }) it('should run the form listener onChange when the field array is changed', () => { let arr!: any - let name!: string const form = new FormApi({ defaultValues: { @@ -2036,9 +2032,8 @@ describe('form api', () => { age: 0, }, listeners: { - onChange: ({ formApi, fieldName }) => { - arr = formApi.state.values[fieldName] - name = fieldName + onChange: ({ fieldApi }) => { + arr = fieldApi.state.value }, }, }) @@ -2052,7 +2047,6 @@ describe('form api', () => { field.removeValue(1) expect(arr).toStrictEqual(['one']) - expect(name).toStrictEqual('items') field.replaceValue(0, 'start') expect(arr).toStrictEqual(['start']) @@ -2071,7 +2065,6 @@ describe('form api', () => { }) it('should run the form listener onBlur', async () => { - let fieldNameCheck!: string let fieldApiCheck!: AnyFieldApi let formApiCheck!: AnyFormApi @@ -2081,8 +2074,7 @@ describe('form api', () => { age: 0, }, listeners: { - onBlur: ({ fieldName, fieldApi, formApi }) => { - fieldNameCheck = fieldName + onBlur: ({ fieldApi, formApi }) => { fieldApiCheck = fieldApi formApiCheck = formApi as any @@ -2098,7 +2090,6 @@ describe('form api', () => { field.mount() field.handleBlur() - expect(fieldNameCheck).toStrictEqual('name') expect(fieldApiCheck.state.value).toStrictEqual('test') expect(formApiCheck.state.values.name).toStrictEqual('test') })