Skip to content
Draft
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
36 changes: 14 additions & 22 deletions packages/form-core/src/FieldApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Derived, batch } from '@tanstack/store'
import { Derived, Store, batch } from '@tanstack/store'
import {
isStandardSchemaValidator,
standardSchemaValidators,
Expand Down Expand Up @@ -864,6 +864,10 @@ export type AnyFieldMeta = FieldMeta<
any
>

export type FieldBaseState<TParentData, TName extends DeepKeys<TParentData>> = {
name: TName
}

/**
* An object type representing the state of a field.
*/
Expand Down Expand Up @@ -1052,7 +1056,7 @@ export class FieldApi<
/**
* The field name.
*/
name!: DeepKeys<TParentData>
name: DeepKeys<TParentData>
/**
* The field options.
*/
Expand Down Expand Up @@ -1151,8 +1155,9 @@ export class FieldApi<
TParentSubmitMeta
>,
) {
this.form = opts.form as never
this.name = opts.name as never
this.form = opts.form
this.name = opts.name

this.timeoutIds = {
validations: {} as Record<ValidationCause, never>,
listeners: {} as Record<ListenerCause, never>,
Expand Down Expand Up @@ -1309,28 +1314,15 @@ export class FieldApi<
TParentSubmitMeta
>,
) => {
this.options = opts as never

const nameHasChanged = this.name !== opts.name
this.options = opts
this.name = opts.name

// Default Value
if ((this.state.value as unknown) === undefined) {
const formDefault = getBy(opts.form.options.defaultValues, opts.name)

const defaultValue = (opts.defaultValue as unknown) ?? formDefault

// The name is dynamic in array fields. It changes when the user performs operations like removing or reordering.
// In this case, we don't want to force a default value if the store managed to find an existing value.
if (nameHasChanged) {
this.setValue((val) => (val as unknown) || defaultValue, {
dontUpdateMeta: true,
})
} else if (defaultValue !== undefined) {
this.setValue(defaultValue as never, {
dontUpdateMeta: true,
})
}
const formDefault = getBy(
opts.form.options.defaultValues,
opts.name,
).value
}

// Default Meta
Expand Down
4 changes: 2 additions & 2 deletions packages/form-core/src/FieldGroupApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,14 +283,14 @@ export class FieldGroupApi<
let values: TFieldGroupData
if (typeof this.fieldsMap === 'string') {
// all values live at that name, so we can directly fetch it
values = getBy(currFormStore.values, this.fieldsMap)
values = getBy(currFormStore.values, this.fieldsMap).value
} else {
// we need to fetch the values from all places where they were mapped from
values = {} as never
const fields: Record<keyof TFieldGroupData, string> = this
.fieldsMap as never
for (const key in fields) {
values[key] = getBy(currFormStore.values, fields[key])
values[key] = getBy(currFormStore.values, fields[key]).value
}
}

Expand Down
32 changes: 17 additions & 15 deletions packages/form-core/src/FormApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,7 @@ export class FormApi<
const prevFieldInfo =
prevVal?.[fieldName as never as keyof typeof prevVal]

const curFieldVal = getBy(currBaseStore.values, fieldName)
const curFieldVal = getBy(currBaseStore.values, fieldName).value

let fieldErrors = prevFieldInfo?.errors
if (
Expand All @@ -1061,25 +1061,23 @@ export class FormApi<
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
fieldErrors = Object.values(currBaseMeta.errorMap ?? {}).filter(
(val) => val !== undefined,
) as never
)

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const fieldInstance = this.getFieldInfo(fieldName)?.instance

if (fieldInstance && !fieldInstance.options.disableErrorFlat) {
fieldErrors = (fieldErrors as undefined | string[])?.flat(
1,
) as never
fieldErrors = fieldErrors.flat(1)
}
}

// As primitives, we don't need to aggressively persist the same referential value for performance reasons
const isFieldValid = !isNonEmptyArray(fieldErrors ?? [])
const isFieldValid = !isNonEmptyArray(fieldErrors)
const isFieldPristine = !currBaseMeta.isDirty
const isDefaultValue =
evaluate(
curFieldVal,
getBy(this.options.defaultValues, fieldName),
getBy(this.options.defaultValues, fieldName).value,
) ||
evaluate(
curFieldVal,
Expand All @@ -1102,11 +1100,11 @@ export class FormApi<

fieldMeta[fieldName] = {
...currBaseMeta,
errors: fieldErrors,
errors: fieldErrors ?? [],
isPristine: isFieldPristine,
isValid: isFieldValid,
isDefaultValue: isDefaultValue,
} as AnyFieldMeta
} satisfies AnyFieldMeta as AnyFieldMeta
}

if (!Object.keys(currBaseStore.fieldMetaBase).length) return fieldMeta
Expand Down Expand Up @@ -2131,7 +2129,7 @@ export class FormApi<
*/
getFieldValue = <TField extends DeepKeys<TFormData>>(
field: TField,
): DeepValue<TFormData, TField> => getBy(this.state.values, field)
): DeepValue<TFormData, TField> => getBy(this.state.values, field).value

/**
* Gets the metadata of the specified field.
Expand Down Expand Up @@ -2304,7 +2302,7 @@ export class FormApi<
await this.validateField(field, 'change')

// Shift down all meta after validating to make sure the new field has been mounted
metaHelper(this).handleArrayFieldMetaShift(field, index, 'insert')
metaHelper(this).handleArrayInsert(field, index)

await this.validateArrayFieldsStartingFrom(field, index, 'change')
}
Expand Down Expand Up @@ -2360,7 +2358,7 @@ export class FormApi<
)

// Shift up all meta
metaHelper(this).handleArrayFieldMetaShift(field, index, 'remove')
metaHelper(this).handleArrayRemove(field, index)

if (lastIndex !== null) {
const start = `${field}[${lastIndex}]`
Expand Down Expand Up @@ -2392,7 +2390,7 @@ export class FormApi<
)

// Swap meta
metaHelper(this).handleArrayFieldMetaShift(field, index1, 'swap', index2)
metaHelper(this).handleArraySwap(field, index1, index2)

// Validate the whole array
this.validateField(field, 'change')
Expand Down Expand Up @@ -2421,7 +2419,7 @@ export class FormApi<
)

// Move meta between index1 and index2
metaHelper(this).handleArrayFieldMetaShift(field, index1, 'move', index2)
metaHelper(this).handleArrayMove(field, index1, index2)

// Validate the whole array
this.validateField(field, 'change')
Expand Down Expand Up @@ -2472,7 +2470,11 @@ export class FormApi<
[field]: defaultFieldMeta,
},
values: this.options.defaultValues
? setBy(prev.values, field, getBy(this.options.defaultValues, field))
? setBy(
prev.values,
field,
getBy(this.options.defaultValues, field).value,
)
: prev.values,
}
})
Expand Down
Loading
Loading