/
useApplyInputDefaultValues.ts
95 lines (84 loc) · 3.01 KB
/
useApplyInputDefaultValues.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { useEffect } from 'react';
import {
FieldValues,
UseFieldArrayReturn,
useFormContext,
} from 'react-hook-form';
import get from 'lodash/get';
import { useRecordContext } from '../controller';
import { InputProps } from './useInput';
interface StandardInput {
inputProps: Partial<InputProps> & { source: string };
isArrayInput?: undefined;
fieldArrayInputControl?: undefined;
}
interface ArrayInput {
inputProps: Partial<InputProps> & { source: string };
isArrayInput: true;
fieldArrayInputControl: UseFieldArrayReturn<FieldValues, string, 'id'>;
}
type Props = StandardInput | ArrayInput;
/*
* This hook updates the input with the default value if default value is present
* and field input is not already populated or dirty
*/
export const useApplyInputDefaultValues = ({
inputProps,
isArrayInput,
fieldArrayInputControl,
}: Props) => {
const { defaultValue, source } = inputProps;
const record = useRecordContext(inputProps);
const {
getValues,
resetField,
getFieldState,
formState,
reset,
} = useFormContext();
const recordValue = get(record, source);
const formValue = get(getValues(), source);
const { isDirty } = getFieldState(source, formState);
useEffect(() => {
if (
defaultValue == null ||
formValue != null ||
recordValue != null ||
isDirty
) {
return;
}
// Side note: For Array Input but checked for all to avoid possible regression
// Since we use get(record, source), if source is like foo.23.bar,
// this effect will run. However we only want to set the default value
// for the subfield bar if the record actually has a value for foo.23
const pathContainsIndex = source
.split('.')
.some(pathPart => numericRegex.test(pathPart));
if (pathContainsIndex) {
const parentPath = source.split('.').slice(0, -1).join('.');
const parentValue = get(getValues(), parentPath);
if (parentValue == null) {
// the parent is undefined, so we don't want to set the default value
return;
}
}
if (isArrayInput) {
if (!fieldArrayInputControl) {
throw new Error(
'useApplyInputDefaultValues: No fieldArrayInputControl passed in props for array input usage'
);
}
// We need to update inputs nested in array using react hook forms
// own array controller rather then the generic reset to prevent control losing
// context of the nested inputs
fieldArrayInputControl.replace(defaultValue);
// resets the form so that control no longer sees the form as dirty after
// defaults applied
reset({}, { keepValues: true });
return;
}
resetField(source, { defaultValue });
});
};
const numericRegex = /^\d+$/;