-
-
Notifications
You must be signed in to change notification settings - Fork 2k
/
useForm.ts
125 lines (114 loc) · 3.39 KB
/
useForm.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import React from 'react';
import { createFormControl } from './logic/createFormControl';
import getProxyFormState from './logic/getProxyFormState';
import shouldRenderFormState from './logic/shouldRenderFormState';
import deepEqual from './utils/deepEqual';
import isFunction from './utils/isFunction';
import {
FieldValues,
FormState,
InternalFieldName,
UseFormProps,
UseFormReturn,
} from './types';
import { useSubscribe } from './useSubscribe';
/**
* Custom hook to manage the entire form.
*
* @remarks
* [API](https://react-hook-form.com/api/useform) • [Demo](https://codesandbox.io/s/react-hook-form-get-started-ts-5ksmm) • [Video](https://www.youtube.com/watch?v=RkXv4AXXC_4)
*
* @param props - form configuration and validation parameters.
*
* @returns methods - individual functions to manage the form state. {@link UseFormReturn}
*
* @example
* ```tsx
* function App() {
* const { register, handleSubmit, watch, formState: { errors } } = useForm();
* const onSubmit = data => console.log(data);
*
* console.log(watch("example"));
*
* return (
* <form onSubmit={handleSubmit(onSubmit)}>
* <input defaultValue="test" {...register("example")} />
* <input {...register("exampleRequired", { required: true })} />
* {errors.exampleRequired && <span>This field is required</span>}
* <input type="submit" />
* </form>
* );
* }
* ```
*/
export function useForm<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
>(
props: UseFormProps<TFieldValues, TContext> = {},
): UseFormReturn<TFieldValues, TContext> {
const _formControl = React.useRef<
UseFormReturn<TFieldValues, TContext> | undefined
>();
const [formState, updateFormState] = React.useState<FormState<TFieldValues>>({
isDirty: false,
isValidating: false,
isLoading: isFunction(props.defaultValues),
isSubmitted: false,
isSubmitting: false,
isSubmitSuccessful: false,
isValid: false,
submitCount: 0,
dirtyFields: {},
touchedFields: {},
errors: {},
defaultValues: isFunction(props.defaultValues)
? undefined
: props.defaultValues,
});
if (!_formControl.current) {
_formControl.current = {
...createFormControl(props, () =>
updateFormState((formState) => ({ ...formState })),
),
formState,
};
}
const control = _formControl.current.control;
control._options = props;
useSubscribe({
subject: control._subjects.state,
next: (
value: Partial<FormState<TFieldValues>> & { name?: InternalFieldName },
) => {
if (
shouldRenderFormState(
value,
control._proxyFormState,
control._updateFormState,
true,
)
) {
updateFormState({ ...control._formState });
}
},
});
React.useEffect(() => {
if (props.values && !deepEqual(props.values, control._defaultValues)) {
control._reset(props.values, control._options.resetOptions);
}
}, [props.values, control]);
React.useEffect(() => {
if (!control._state.mount) {
control._updateValid();
control._state.mount = true;
}
if (control._state.watch) {
control._state.watch = false;
control._subjects.state.next({ ...control._formState });
}
control._removeUnmounted();
});
_formControl.current.formState = getProxyFormState(formState, control);
return _formControl.current;
}