Skip to content
Open
6 changes: 5 additions & 1 deletion packages/form-core/src/FormApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2267,7 +2267,11 @@ export class FormApi<
this.baseStore.setState((prev) => {
return {
...prev,
values: setBy(prev.values, field, updater),
values: setBy(
prev.values,
field,
updater
),
}
})
})
Expand Down
16 changes: 15 additions & 1 deletion packages/form-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,16 @@ export function getBy(obj: unknown, path: string | (string | number)[]): any {
* @private
*/
export function setBy(obj: any, _path: any, updater: Updater<any>) {
const isFile = updater instanceof File
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Native does not have File defined and will throw a ReferenceError. You cannot implement the fix this way.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Truly. Good catch, thanks!


const path = makePathArray(_path)

function doSet(parent?: any): any {
if (!path.length) {
return functionalUpdate(updater, parent)
return functionalUpdate(
isFile ? { file: updater, uuid: uuid() } : updater,
parent,
)
}

const key = path.shift()
Expand Down Expand Up @@ -422,6 +427,15 @@ export const isGlobalFormValidationError = (
}

export function evaluate<T>(objA: T, objB: T) {
if (objA instanceof File && objB instanceof File) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the previous comment wasn't addressed, despite the request for another review. This will throw ReferenceErrors in environments where File is not present (such as React Native).

return (
objA.name === objB.name &&
objA.size === objB.size &&
objA.type === objB.type &&
objA.lastModified === objB.lastModified
)
}

if (Object.is(objA, objB)) {
return true
}
Expand Down
31 changes: 31 additions & 0 deletions packages/form-core/tests/FormApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4096,6 +4096,37 @@ it('should generate a formId if not provided', () => {
expect(form.formId.length).toBeGreaterThan(1)
})

it('should set a file value with uuid when setting a field value with File', () => {
const form = new FormApi({
defaultValues: {
avatar: undefined,
} as { avatar: unknown },
})

form.mount()

const firstFile = new File(['first'], 'first.png', { type: 'image/png' })
form.setFieldValue('avatar', firstFile)

const firstValue = form.state.values.avatar as { file: File; uuid: string }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These type assertions are a huge problem. It cannot stay.

Luckily, as long as evaluate properly handles File types, this whole uuid trickery won't be needed. Just make sure to add unit tests for a File comparison with evaluate. I believe the existing tests can be found in utils.spec.ts.


expect(firstValue).toBeDefined()
expect(firstValue.file instanceof File).toBe(true)
expect(firstValue.file.name).toBe('first.png')
expect(typeof firstValue.uuid).toBe('string')
expect(firstValue.uuid.length).toBeGreaterThan(0)

const secondFile = new File(['second'], 'second.png', { type: 'image/png' })
form.setFieldValue('avatar', secondFile)

const secondValue = form.state.values.avatar as { file: File; uuid: string }

expect(secondValue.file.name).toBe('second.png')
expect(typeof secondValue.uuid).toBe('string')
expect(secondValue.uuid.length).toBeGreaterThan(0)
expect(secondValue.uuid).not.toBe(firstValue.uuid)
})

describe('form api event client', () => {
it('should have debug disabled', () => {
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
Expand Down
Loading