Skip to content

fix(form-core): Trigger listeners and validation on setFieldValue #1680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 86 additions & 25 deletions packages/form-core/src/FieldApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getAsyncValidatorArray,
getBy,
getSyncValidatorArray,
mergeOpts,
} from './utils'
import { defaultValidationLogic } from './ValidationLogic'
import type { DeepKeys, DeepValue, UnwrapOneLevelOfArray } from './util-types'
Expand Down Expand Up @@ -1350,11 +1351,19 @@ export class FieldApi<
* Sets the field value and run the `change` validator.
*/
setValue = (updater: Updater<TData>, options?: UpdateMetaOptions) => {
this.form.setFieldValue(this.name, updater as never, options)
this.form.setFieldValue(
this.name,
updater as never,
mergeOpts(options, { dontRunListeners: true, dontValidate: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}

this.validate('change')
if (!options?.dontValidate) {
this.validate('change')
}
}

getMeta = () => this.store.state.meta
Expand Down Expand Up @@ -1400,11 +1409,17 @@ export class FieldApi<
*/
pushValue = (
value: TData extends any[] ? TData[number] : never,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.form.pushFieldValue(this.name, value as any, opts)
this.form.pushFieldValue(
this.name,
value as any,
mergeOpts(options, { dontRunListeners: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}
}

/**
Expand All @@ -1413,11 +1428,18 @@ export class FieldApi<
insertValue = (
index: number,
value: TData extends any[] ? TData[number] : never,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.form.insertFieldValue(this.name, index, value as any, opts)
this.form.insertFieldValue(
this.name,
index,
value as any,
mergeOpts(options, { dontRunListeners: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}
}

/**
Expand All @@ -1426,47 +1448,83 @@ export class FieldApi<
replaceValue = (
index: number,
value: TData extends any[] ? TData[number] : never,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.form.replaceFieldValue(this.name, index, value as any, opts)
this.form.replaceFieldValue(
this.name,
index,
value as any,
mergeOpts(options, { dontRunListeners: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}
}

/**
* Removes a value at the specified index.
*/
removeValue = (index: number, opts?: UpdateMetaOptions) => {
this.form.removeFieldValue(this.name, index, opts)
removeValue = (index: number, options?: UpdateMetaOptions) => {
this.form.removeFieldValue(
this.name,
index,
mergeOpts(options, { dontRunListeners: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}
}

/**
* Swaps the values at the specified indices.
*/
swapValues = (aIndex: number, bIndex: number, opts?: UpdateMetaOptions) => {
this.form.swapFieldValues(this.name, aIndex, bIndex, opts)
swapValues = (
aIndex: number,
bIndex: number,
options?: UpdateMetaOptions,
) => {
this.form.swapFieldValues(
this.name,
aIndex,
bIndex,
mergeOpts(options, { dontRunListeners: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}
}

/**
* Moves the value at the first specified index to the second specified index.
*/
moveValue = (aIndex: number, bIndex: number, opts?: UpdateMetaOptions) => {
this.form.moveFieldValues(this.name, aIndex, bIndex, opts)
moveValue = (aIndex: number, bIndex: number, options?: UpdateMetaOptions) => {
this.form.moveFieldValues(
this.name,
aIndex,
bIndex,
mergeOpts(options, { dontRunListeners: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}
}

/**
* Clear all values from the array.
*/
clearValues = (opts?: UpdateMetaOptions) => {
this.form.clearFieldValues(this.name, opts)
clearValues = (options?: UpdateMetaOptions) => {
this.form.clearFieldValues(
this.name,
mergeOpts(options, { dontRunListeners: true }),
)

this.triggerOnChangeListener()
if (!options?.dontRunListeners) {
this.triggerOnChangeListener()
}
}

/**
Expand Down Expand Up @@ -1936,7 +1994,10 @@ export class FieldApi<
}
}

private triggerOnChangeListener() {
/**
* @private
*/
triggerOnChangeListener() {
const formDebounceMs = this.form.options.listeners?.onChangeDebounceMs
if (formDebounceMs && formDebounceMs > 0) {
if (this.timeoutIds.formListeners.change) {
Expand Down
60 changes: 45 additions & 15 deletions packages/form-core/src/FormApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getSyncValidatorArray,
isGlobalFormValidationError,
isNonEmptyArray,
mergeOpts,
setBy,
} from './utils'
import { defaultValidationLogic } from './ValidationLogic'
Expand All @@ -35,6 +36,7 @@ import type {
FieldManipulator,
FormValidationError,
FormValidationErrorMap,
ListenerCause,
UpdateMetaOptions,
ValidationCause,
ValidationError,
Expand Down Expand Up @@ -930,6 +932,15 @@ export class FormApi<
*/
prevTransformArray: unknown[] = []

/**
*
*/
timeoutIds: {
validations: Record<ValidationCause, ReturnType<typeof setTimeout> | null>
listeners: Record<ListenerCause, ReturnType<typeof setTimeout> | null>
formListeners: Record<ListenerCause, ReturnType<typeof setTimeout> | null>
}

/**
* Constructs a new `FormApi` instance with the given form options.
*/
Expand All @@ -949,6 +960,12 @@ export class FormApi<
TSubmitMeta
>,
) {
this.timeoutIds = {
validations: {} as Record<ValidationCause, never>,
listeners: {} as Record<ListenerCause, never>,
formListeners: {} as Record<ListenerCause, never>,
}

this.baseStore = new Store(
getDefaultFormState({
...(opts?.defaultState as any),
Expand Down Expand Up @@ -2056,6 +2073,8 @@ export class FormApi<
opts?: UpdateMetaOptions,
) => {
const dontUpdateMeta = opts?.dontUpdateMeta ?? false
const dontRunListeners = opts?.dontRunListeners ?? false
const dontValidate = opts?.dontValidate ?? false

batch(() => {
if (!dontUpdateMeta) {
Expand All @@ -2078,6 +2097,14 @@ export class FormApi<
}
})
})

if (!dontRunListeners) {
this.getFieldInfo(field).instance?.triggerOnChangeListener()
}

if (!dontValidate) {
this.validateField(field, 'change')
}
}

deleteField = <TField extends DeepKeys<TFormData>>(field: TField) => {
Expand Down Expand Up @@ -2109,14 +2136,13 @@ export class FormApi<
value: DeepValue<TFormData, TField> extends any[]
? DeepValue<TFormData, TField>[number]
: never,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.setFieldValue(
field,
(prev) => [...(Array.isArray(prev) ? prev : []), value] as any,
opts,
options,
)
this.validateField(field, 'change')
}

insertFieldValue = async <TField extends DeepKeysOfType<TFormData, any[]>>(
Expand All @@ -2125,7 +2151,7 @@ export class FormApi<
value: DeepValue<TFormData, TField> extends any[]
? DeepValue<TFormData, TField>[number]
: never,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.setFieldValue(
field,
Expand All @@ -2136,7 +2162,7 @@ export class FormApi<
...(prev as DeepValue<TFormData, TField>[]).slice(index),
] as any
},
opts,
mergeOpts(options, { dontValidate: true }),
)

// Validate the whole array + all fields that have shifted
Expand All @@ -2157,7 +2183,7 @@ export class FormApi<
value: DeepValue<TFormData, TField> extends any[]
? DeepValue<TFormData, TField>[number]
: never,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.setFieldValue(
field,
Expand All @@ -2166,7 +2192,7 @@ export class FormApi<
i === index ? value : d,
) as any
},
opts,
mergeOpts(options, { dontValidate: true }),
)

// Validate the whole array + all fields that have shifted
Expand All @@ -2180,7 +2206,7 @@ export class FormApi<
removeFieldValue = async <TField extends DeepKeysOfType<TFormData, any[]>>(
field: TField,
index: number,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
const fieldValue = this.getFieldValue(field)

Expand All @@ -2195,7 +2221,7 @@ export class FormApi<
(_d, i) => i !== index,
) as any
},
opts,
mergeOpts(options, { dontValidate: true }),
)

// Shift up all meta
Expand All @@ -2218,7 +2244,7 @@ export class FormApi<
field: TField,
index1: number,
index2: number,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.setFieldValue(
field,
Expand All @@ -2227,7 +2253,7 @@ export class FormApi<
const prev2 = prev[index2]!
return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)
},
opts,
mergeOpts(options, { dontValidate: true }),
)

// Swap meta
Expand All @@ -2247,7 +2273,7 @@ export class FormApi<
field: TField,
index1: number,
index2: number,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
this.setFieldValue(
field,
Expand All @@ -2256,7 +2282,7 @@ export class FormApi<
next.splice(index2, 0, next.splice(index1, 1)[0])
return next
},
opts,
mergeOpts(options, { dontValidate: true }),
)

// Move meta between index1 and index2
Expand All @@ -2274,15 +2300,19 @@ export class FormApi<
*/
clearFieldValues = <TField extends DeepKeysOfType<TFormData, any[]>>(
field: TField,
opts?: UpdateMetaOptions,
options?: UpdateMetaOptions,
) => {
const fieldValue = this.getFieldValue(field)

const lastIndex = Array.isArray(fieldValue)
? Math.max((fieldValue as unknown[]).length - 1, 0)
: null

this.setFieldValue(field, [] as any, opts)
this.setFieldValue(
field,
[] as any,
mergeOpts(options, { dontValidate: true }),
)

if (lastIndex !== null) {
for (let i = 0; i <= lastIndex; i++) {
Expand Down
8 changes: 8 additions & 0 deletions packages/form-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ export interface UpdateMetaOptions {
* @default false
*/
dontUpdateMeta?: boolean
/**
* @default false
*/
dontValidate?: boolean
/**
* @default false
*/
dontRunListeners?: boolean
}

/**
Expand Down
Loading