Skip to content

Commit 37d659c

Browse files
committed
add 'found' property to getBy
1 parent b0e5206 commit 37d659c

File tree

4 files changed

+54
-19
lines changed

4 files changed

+54
-19
lines changed

packages/form-core/src/FieldGroupApi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,14 @@ export class FieldGroupApi<
232232
let values: TFieldGroupData
233233
if (typeof this.fieldsMap === 'string') {
234234
// all values live at that name, so we can directly fetch it
235-
values = getBy(currFormStore.values, this.fieldsMap)
235+
values = getBy(currFormStore.values, this.fieldsMap).value
236236
} else {
237237
// we need to fetch the values from all places where they were mapped from
238238
values = {} as never
239239
const fields: Record<keyof TFieldGroupData, string> = this
240240
.fieldsMap as never
241241
for (const key in fields) {
242-
values[key] = getBy(currFormStore.values, fields[key])
242+
values[key] = getBy(currFormStore.values, fields[key]).value
243243
}
244244
}
245245

packages/form-core/src/FormApi.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ export class FormApi<
10001000
const prevFieldInfo =
10011001
prevVal?.[fieldName as never as keyof typeof prevVal]
10021002

1003-
const curFieldVal = getBy(currBaseStore.values, fieldName)
1003+
const curFieldVal = getBy(currBaseStore.values, fieldName).value
10041004

10051005
let fieldErrors = prevFieldInfo?.errors
10061006
if (
@@ -1028,7 +1028,7 @@ export class FormApi<
10281028
const isDefaultValue =
10291029
evaluate(
10301030
curFieldVal,
1031-
getBy(this.options.defaultValues, fieldName),
1031+
getBy(this.options.defaultValues, fieldName).value,
10321032
) ||
10331033
evaluate(
10341034
curFieldVal,
@@ -1995,7 +1995,7 @@ export class FormApi<
19951995
*/
19961996
getFieldValue = <TField extends DeepKeys<TFormData>>(
19971997
field: TField,
1998-
): DeepValue<TFormData, TField> => getBy(this.state.values, field)
1998+
): DeepValue<TFormData, TField> => getBy(this.state.values, field).value
19991999

20002000
/**
20012001
* Gets the metadata of the specified field.
@@ -2323,7 +2323,7 @@ export class FormApi<
23232323
[field]: defaultFieldMeta,
23242324
},
23252325
values: this.options.defaultValues
2326-
? setBy(prev.values, field, getBy(this.options.defaultValues, field))
2326+
? setBy(prev.values, field, getBy(this.options.defaultValues, field).value)
23272327
: prev.values,
23282328
}
23292329
})

packages/form-core/src/utils.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,21 @@ export function functionalUpdate<TInput, TOutput = TInput>(
3131
* Get a value from an object using a path, including dot notation.
3232
* @private
3333
*/
34-
export function getBy(obj: any, path: any) {
34+
export function getBy(obj: unknown, path: string | (string | number)[]): { found: boolean; value: any } {
3535
const pathObj = makePathArray(path)
36-
return pathObj.reduce((current: any, pathPart: any) => {
37-
if (current === null) return null
38-
if (typeof current !== 'undefined') {
39-
return current[pathPart]
36+
let current: unknown = obj
37+
for (const pathPart of pathObj) {
38+
// path is trying to access props of undefined/null, so it doesn't exist
39+
if (typeof current === 'undefined' || current === null) {
40+
return { found: false, value: undefined }
4041
}
41-
return undefined
42-
}, obj)
42+
if (typeof current === 'object' && (pathPart in current)) {
43+
current = current[pathPart as never]
44+
} else {
45+
return { found: false, value: undefined }
46+
}
47+
}
48+
return { found: true, value: current };
4349
}
4450

4551
/**

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

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,54 @@ describe('getBy', () => {
2121
mother: {
2222
name: 'Lisa',
2323
},
24+
siblings: [{
25+
name: undefined
26+
}]
2427
}
2528

2629
it('should get subfields by path', () => {
27-
expect(getBy(structure, 'name')).toBe(structure.name)
28-
expect(getBy(structure, 'mother.name')).toBe(structure.mother.name)
30+
const name = getBy(structure, 'name');
31+
expect(name.value).toBe(structure.name)
32+
expect(name.found).toBe(true)
33+
34+
const motherName = getBy(structure, 'mother.name');
35+
expect(motherName.value).toBe(structure.mother.name)
36+
expect(motherName.found).toBe(true)
2937
})
3038

3139
it('should get array subfields by path', () => {
32-
expect(getBy(structure, 'kids[0].name')).toBe(structure.kids[0]!.name)
33-
expect(getBy(structure, 'kids[0].age')).toBe(structure.kids[0]!.age)
40+
const kidsName = getBy(structure, 'kids[0].name')
41+
expect(kidsName.value).toBe(structure.kids[0]!.name)
42+
expect(kidsName.found).toBe(true)
43+
44+
const kidsAge = getBy(structure, 'kids[0].age')
45+
expect(kidsAge.value).toBe(structure.kids[0]!.age)
46+
expect(kidsAge.found).toBe(true)
3447
})
3548

3649
it('should get nested array subfields by path', () => {
37-
expect(getBy(structure, 'kids[0].hobbies[0]')).toBe(
50+
const hobbies0 = getBy(structure, 'kids[0].hobbies[0]');
51+
expect(hobbies0.value).toBe(
3852
structure.kids[0]!.hobbies[0],
3953
)
40-
expect(getBy(structure, 'kids[0].hobbies[1]')).toBe(
54+
expect(hobbies0.found).toBe(true)
55+
56+
const hobbies1 = getBy(structure, 'kids[0].hobbies[1]');
57+
expect(hobbies1.value).toBe(
4158
structure.kids[0]!.hobbies[1],
4259
)
60+
expect(hobbies1.found).toBe(true)
61+
})
62+
63+
it('should differentiate between explicit undefined vs. no path', () => {
64+
const sibling0 = getBy(structure, 'siblings[0].name');
65+
const sibling1 = getBy(structure, 'siblings[1].name');
66+
67+
expect(sibling0.value).toBeUndefined()
68+
expect(sibling1.value).toBeUndefined()
69+
70+
expect(sibling0.found).toBe(true)
71+
expect(sibling1.found).toBe(false)
4372
})
4473
})
4574

0 commit comments

Comments
 (0)