Skip to content

Commit 5ec30b3

Browse files
committed
change: Remove isFile helper and update File/Blob equality logic
1 parent d2f4bb2 commit 5ec30b3

File tree

3 files changed

+56
-44
lines changed

3 files changed

+56
-44
lines changed

packages/form-core/src/utils.ts

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,6 @@ export function getBy(obj: unknown, path: string | (string | number)[]): any {
4343
}, obj)
4444
}
4545

46-
/**
47-
* Check if an object is a File.
48-
* @private
49-
*/
50-
export function isFile(obj: any) {
51-
return (
52-
obj &&
53-
typeof obj === 'object' &&
54-
'name' in obj &&
55-
'size' in obj &&
56-
'type' in obj
57-
)
58-
}
59-
6046
/**
6147
* Set a value on an object using a path, including dot notation.
6248
* @private
@@ -66,10 +52,7 @@ export function setBy(obj: any, _path: any, updater: Updater<any>) {
6652

6753
function doSet(parent?: any): any {
6854
if (!path.length) {
69-
return functionalUpdate(
70-
isFile(updater) ? { file: updater, uuid: uuid() } : updater,
71-
parent,
72-
)
55+
return functionalUpdate(updater, parent)
7356
}
7457

7558
const key = path.shift()
@@ -439,15 +422,6 @@ export const isGlobalFormValidationError = (
439422
}
440423

441424
export function evaluate<T>(objA: T, objB: T) {
442-
if (objA instanceof File && objB instanceof File) {
443-
return (
444-
objA.name === objB.name &&
445-
objA.size === objB.size &&
446-
objA.type === objB.type &&
447-
objA.lastModified === objB.lastModified
448-
)
449-
}
450-
451425
if (Object.is(objA, objB)) {
452426
return true
453427
}
@@ -461,6 +435,18 @@ export function evaluate<T>(objA: T, objB: T) {
461435
return false
462436
}
463437

438+
// Blob (and File, which extends Blob) objects have no own enumerable keys,
439+
// so the generic key-comparison below would incorrectly consider any two
440+
// Blob/File instances as equal. Fall back to referential identity (already
441+
// handled by Object.is above, which returned false).
442+
if (
443+
typeof Blob !== 'undefined' &&
444+
objA instanceof Blob &&
445+
objB instanceof Blob
446+
) {
447+
return false
448+
}
449+
464450
if (objA instanceof Date && objB instanceof Date) {
465451
return objA.getTime() === objB.getTime()
466452
}

packages/form-core/tests/FormApi.spec.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4101,35 +4101,30 @@ it('should generate a formId if not provided', () => {
41014101
expect(form.formId.length).toBeGreaterThan(1)
41024102
})
41034103

4104-
it('should set a file value with uuid when setting a field value with File', () => {
4104+
it('should detect file value changes when setting a different File', () => {
41054105
const form = new FormApi({
41064106
defaultValues: {
4107-
avatar: undefined,
4108-
} as { avatar: unknown },
4107+
avatar: undefined as File | undefined,
4108+
},
41094109
})
41104110

41114111
form.mount()
41124112

41134113
const firstFile = new File(['first'], 'first.png', { type: 'image/png' })
41144114
form.setFieldValue('avatar', firstFile)
4115-
4116-
const firstValue = form.state.values.avatar as { file: File; uuid: string }
4117-
4118-
expect(firstValue).toBeDefined()
4119-
expect(isFile(firstValue.file)).toBe(true)
4120-
expect(firstValue.file.name).toBe('first.png')
4121-
expect(typeof firstValue.uuid).toBe('string')
4122-
expect(firstValue.uuid.length).toBeGreaterThan(0)
4115+
expect(form.getFieldValue('avatar')).toBe(firstFile)
4116+
expect(form.getFieldValue('avatar')).toBeInstanceOf(File)
4117+
expect(form.getFieldValue('avatar')!.name).toBe('first.png')
41234118

41244119
const secondFile = new File(['second'], 'second.png', { type: 'image/png' })
41254120
form.setFieldValue('avatar', secondFile)
4121+
expect(form.getFieldValue('avatar')).toBe(secondFile)
4122+
expect(form.getFieldValue('avatar')).toBeInstanceOf(File)
4123+
expect(form.getFieldValue('avatar')!.name).toBe('second.png')
41264124

4127-
const secondValue = form.state.values.avatar as { file: File; uuid: string }
4128-
4129-
expect(secondValue.file.name).toBe('second.png')
4130-
expect(typeof secondValue.uuid).toBe('string')
4131-
expect(secondValue.uuid.length).toBeGreaterThan(0)
4132-
expect(secondValue.uuid).not.toBe(firstValue.uuid)
4125+
// Setting the same file reference again should keep the same value
4126+
form.setFieldValue('avatar', secondFile)
4127+
expect(form.getFieldValue('avatar')).toBe(secondFile)
41334128
})
41344129

41354130
describe('form api event client', () => {

packages/form-core/tests/utils.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,37 @@ describe('evaluate', () => {
753753
const setB = new Set([1, 2, 4])
754754
expect(evaluate(setA, setB)).toEqual(false)
755755
})
756+
757+
it('should test equality between File/Blob objects', () => {
758+
// Same reference should be equal
759+
const file1 = new File(['content'], 'test.txt', { type: 'text/plain' })
760+
expect(evaluate(file1, file1)).toEqual(true)
761+
762+
// Different File objects with same metadata should NOT be equal
763+
// (referential identity — the user picked a new file)
764+
const file2 = new File(['content'], 'test.txt', { type: 'text/plain' })
765+
expect(evaluate(file1, file2)).toEqual(false)
766+
767+
// Different File objects with different metadata
768+
const file3 = new File(['other'], 'other.txt', { type: 'image/png' })
769+
expect(evaluate(file1, file3)).toEqual(false)
770+
771+
// Blob objects
772+
const blob1 = new Blob(['data'], { type: 'application/octet-stream' })
773+
const blob2 = new Blob(['data'], { type: 'application/octet-stream' })
774+
expect(evaluate(blob1, blob1)).toEqual(true)
775+
expect(evaluate(blob1, blob2)).toEqual(false)
776+
777+
// File inside an object structure
778+
const obj1 = { avatar: file1 }
779+
const obj2 = { avatar: file2 }
780+
expect(evaluate(obj1, obj2)).toEqual(false)
781+
782+
// Same file ref inside an object structure
783+
const obj3 = { avatar: file1 }
784+
const obj4 = { avatar: file1 }
785+
expect(evaluate(obj3, obj4)).toEqual(true)
786+
})
756787
})
757788

758789
describe('concatenatePaths', () => {

0 commit comments

Comments
 (0)