react-hook-form 8.0.0-alpha.0 → 8.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +387 -317
- package/dist/controller.d.ts +42 -0
- package/dist/controller.d.ts.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.mjs +694 -333
- package/dist/index.esm.mjs.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/logic/createFormControl.d.ts +1 -1
- package/dist/logic/createFormControl.d.ts.map +1 -1
- package/dist/logic/focusFieldBy.d.ts.map +1 -1
- package/dist/logic/getResolverOptions.d.ts +1 -1
- package/dist/logic/shouldRenderFormState.d.ts.map +1 -1
- package/dist/logic/updateFieldArrayRootError.d.ts +4 -0
- package/dist/logic/updateFieldArrayRootError.d.ts.map +1 -0
- package/dist/logic/validateField.d.ts +1 -1
- package/dist/logic/validateField.d.ts.map +1 -1
- package/dist/types/controller.d.ts +24 -3
- package/dist/types/controller.d.ts.map +1 -1
- package/dist/types/errors.d.ts +9 -2
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/fieldArray.d.ts +173 -8
- package/dist/types/fieldArray.d.ts.map +1 -1
- package/dist/types/form.d.ts +395 -29
- package/dist/types/form.d.ts.map +1 -1
- package/dist/types/resolvers.d.ts +3 -3
- package/dist/types/resolvers.d.ts.map +1 -1
- package/dist/types/utils.d.ts +4 -5
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types/validator.d.ts +1 -1
- package/dist/types/validator.d.ts.map +1 -1
- package/dist/useController.d.ts +24 -0
- package/dist/useController.d.ts.map +1 -1
- package/dist/useFieldArray.d.ts +39 -2
- package/dist/useFieldArray.d.ts.map +1 -1
- package/dist/useForm.d.ts +30 -1
- package/dist/useForm.d.ts.map +1 -1
- package/dist/useFormContext.d.ts +62 -2
- package/dist/useFormContext.d.ts.map +1 -1
- package/dist/useFormState.d.ts +30 -0
- package/dist/useFormState.d.ts.map +1 -1
- package/dist/useWatch.d.ts +68 -4
- package/dist/useWatch.d.ts.map +1 -1
- package/dist/utils/compact.d.ts.map +1 -1
- package/dist/utils/unset.d.ts.map +1 -1
- package/package.json +21 -21
package/dist/index.esm.mjs
CHANGED
@@ -22,7 +22,7 @@ var getNodeParentName = (name) => name.substring(0, name.search(/.\d/)) || name;
|
|
22
22
|
|
23
23
|
var isNameInFieldArray = (names, name) => [...names].some((current) => getNodeParentName(name) === current);
|
24
24
|
|
25
|
-
var compact = (value) => value.filter(Boolean);
|
25
|
+
var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
|
26
26
|
|
27
27
|
var isUndefined = (val) => val === undefined;
|
28
28
|
|
@@ -60,15 +60,72 @@ const INPUT_VALIDATION_RULES = {
|
|
60
60
|
validate: 'validate',
|
61
61
|
};
|
62
62
|
|
63
|
-
var omit = (source, key) => {
|
64
|
-
const copy = Object.assign({}, source);
|
65
|
-
delete copy[key];
|
66
|
-
return copy;
|
67
|
-
};
|
68
|
-
|
69
63
|
const HookFormContext = React.createContext(null);
|
64
|
+
/**
|
65
|
+
* This custom hook allows you to access the form context. useFormContext is intended to be used in deeply nested structures, where it would become inconvenient to pass the context as a prop. To be used with {@link FormProvider}.
|
66
|
+
*
|
67
|
+
* @remarks
|
68
|
+
* [API](https://react-hook-form.com/api/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
|
69
|
+
*
|
70
|
+
* @returns return all useForm methods
|
71
|
+
*
|
72
|
+
* @example
|
73
|
+
* ```tsx
|
74
|
+
* function App() {
|
75
|
+
* const methods = useForm();
|
76
|
+
* const onSubmit = data => console.log(data);
|
77
|
+
*
|
78
|
+
* return (
|
79
|
+
* <FormProvider {...methods} >
|
80
|
+
* <form onSubmit={methods.handleSubmit(onSubmit)}>
|
81
|
+
* <NestedInput />
|
82
|
+
* <input type="submit" />
|
83
|
+
* </form>
|
84
|
+
* </FormProvider>
|
85
|
+
* );
|
86
|
+
* }
|
87
|
+
*
|
88
|
+
* function NestedInput() {
|
89
|
+
* const { register } = useFormContext(); // retrieve all hook methods
|
90
|
+
* return <input {...register("test")} />;
|
91
|
+
* }
|
92
|
+
* ```
|
93
|
+
*/
|
70
94
|
const useFormContext = () => React.useContext(HookFormContext);
|
71
|
-
|
95
|
+
/**
|
96
|
+
* A provider component that propagates the `useForm` methods to all children components via [React Context](https://reactjs.org/docs/context.html) API. To be used with {@link useFormContext}.
|
97
|
+
*
|
98
|
+
* @remarks
|
99
|
+
* [API](https://react-hook-form.com/api/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
|
100
|
+
*
|
101
|
+
* @param props - all useFrom methods
|
102
|
+
*
|
103
|
+
* @example
|
104
|
+
* ```tsx
|
105
|
+
* function App() {
|
106
|
+
* const methods = useForm();
|
107
|
+
* const onSubmit = data => console.log(data);
|
108
|
+
*
|
109
|
+
* return (
|
110
|
+
* <FormProvider {...methods} >
|
111
|
+
* <form onSubmit={methods.handleSubmit(onSubmit)}>
|
112
|
+
* <NestedInput />
|
113
|
+
* <input type="submit" />
|
114
|
+
* </form>
|
115
|
+
* </FormProvider>
|
116
|
+
* );
|
117
|
+
* }
|
118
|
+
*
|
119
|
+
* function NestedInput() {
|
120
|
+
* const { register } = useFormContext(); // retrieve all hook methods
|
121
|
+
* return <input {...register("test")} />;
|
122
|
+
* }
|
123
|
+
* ```
|
124
|
+
*/
|
125
|
+
const FormProvider = (props) => {
|
126
|
+
const { children, ...data } = props;
|
127
|
+
return (React.createElement(HookFormContext.Provider, { value: data }, props.children));
|
128
|
+
};
|
72
129
|
|
73
130
|
var getProxyFormState = (formState, _proxyFormState, localProxyFormState, isRoot = true) => {
|
74
131
|
const result = {};
|
@@ -90,7 +147,7 @@ var getProxyFormState = (formState, _proxyFormState, localProxyFormState, isRoot
|
|
90
147
|
var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
|
91
148
|
|
92
149
|
var shouldRenderFormState = (formStateData, _proxyFormState, isRoot) => {
|
93
|
-
const formState =
|
150
|
+
const { name, ...formState } = formStateData;
|
94
151
|
return (isEmptyObject(formState) ||
|
95
152
|
Object.keys(formState).length >= Object.keys(_proxyFormState).length ||
|
96
153
|
Object.keys(formState).find((key) => _proxyFormState[key] ===
|
@@ -125,6 +182,36 @@ function useSubscribe(props) {
|
|
125
182
|
}, [props.disabled]);
|
126
183
|
}
|
127
184
|
|
185
|
+
/**
|
186
|
+
* This custom hook allows you to subscribe to each form state, and isolate the re-render at the custom hook level. It has its scope in terms of form state subscription, so it would not affect other useFormState and useForm. Using this hook can reduce the re-render impact on large and complex form application.
|
187
|
+
*
|
188
|
+
* @remarks
|
189
|
+
* [API](https://react-hook-form.com/api/useformstate) • [Demo](https://codesandbox.io/s/useformstate-75xly)
|
190
|
+
*
|
191
|
+
* @param props - include options on specify fields to subscribe. {@link UseFormStateReturn}
|
192
|
+
*
|
193
|
+
* @example
|
194
|
+
* ```tsx
|
195
|
+
* function App() {
|
196
|
+
* const { register, handleSubmit, control } = useForm({
|
197
|
+
* defaultValues: {
|
198
|
+
* firstName: "firstName"
|
199
|
+
* }});
|
200
|
+
* const { dirtyFields } = useFormState({
|
201
|
+
* control
|
202
|
+
* });
|
203
|
+
* const onSubmit = (data) => console.log(data);
|
204
|
+
*
|
205
|
+
* return (
|
206
|
+
* <form onSubmit={handleSubmit(onSubmit)}>
|
207
|
+
* <input {...register("firstName")} placeholder="First Name" />
|
208
|
+
* {dirtyFields.firstName && <p>Field is dirty.</p>}
|
209
|
+
* <input type="submit" />
|
210
|
+
* </form>
|
211
|
+
* );
|
212
|
+
* }
|
213
|
+
* ```
|
214
|
+
*/
|
128
215
|
function useFormState(props) {
|
129
216
|
const methods = useFormContext();
|
130
217
|
const { control = methods.control, disabled, name, exact } = props || {};
|
@@ -143,14 +230,20 @@ function useFormState(props) {
|
|
143
230
|
const callback = React.useCallback((value) => _mounted.current &&
|
144
231
|
shouldSubscribeByName(_name.current, value.name, exact) &&
|
145
232
|
shouldRenderFormState(value, _localProxyFormState.current) &&
|
146
|
-
updateFormState(
|
233
|
+
updateFormState({
|
234
|
+
...control._formState,
|
235
|
+
...value,
|
236
|
+
}), [control, exact]);
|
147
237
|
useSubscribe({
|
148
238
|
disabled,
|
149
239
|
callback,
|
150
240
|
subject: control._subjects.state,
|
151
241
|
});
|
152
|
-
React.useEffect(() =>
|
153
|
-
_mounted.current =
|
242
|
+
React.useEffect(() => {
|
243
|
+
_mounted.current = true;
|
244
|
+
return () => {
|
245
|
+
_mounted.current = false;
|
246
|
+
};
|
154
247
|
}, []);
|
155
248
|
return getProxyFormState(formState, control._proxyFormState, _localProxyFormState.current, false);
|
156
249
|
}
|
@@ -182,6 +275,19 @@ var objectHasFunction = (data) => {
|
|
182
275
|
return false;
|
183
276
|
};
|
184
277
|
|
278
|
+
/**
|
279
|
+
* Custom hook to subscribe to field change and isolate re-rendering at the component level.
|
280
|
+
*
|
281
|
+
* @remarks
|
282
|
+
*
|
283
|
+
* [API](https://react-hook-form.com/api/usewatch) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-usewatch-h9i5e)
|
284
|
+
*
|
285
|
+
* @example
|
286
|
+
* ```tsx
|
287
|
+
* // can skip passing down the control into useWatch if the form is wrapped with the FormProvider
|
288
|
+
* const values = useWatch()
|
289
|
+
* ```
|
290
|
+
*/
|
185
291
|
function useWatch(props) {
|
186
292
|
const methods = useFormContext();
|
187
293
|
const { control = methods.control, name, defaultValue, disabled, exact, } = props || {};
|
@@ -192,11 +298,12 @@ function useWatch(props) {
|
|
192
298
|
const fieldValues = generateWatchOutput(_name.current, control._names, formState.values || control._formValues);
|
193
299
|
updateValue(isUndefined(_name.current) ||
|
194
300
|
(isObject(fieldValues) && !objectHasFunction(fieldValues))
|
195
|
-
?
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
301
|
+
? { ...fieldValues }
|
302
|
+
: Array.isArray(fieldValues)
|
303
|
+
? [...fieldValues]
|
304
|
+
: isUndefined(fieldValues)
|
305
|
+
? defaultValue
|
306
|
+
: fieldValues);
|
200
307
|
}
|
201
308
|
}, [control, exact, defaultValue]);
|
202
309
|
useSubscribe({
|
@@ -213,6 +320,30 @@ function useWatch(props) {
|
|
213
320
|
return value;
|
214
321
|
}
|
215
322
|
|
323
|
+
/**
|
324
|
+
* Custom hook to work with controlled component, this function provide you with both form and field level state. Re-render is isolated at the hook level.
|
325
|
+
*
|
326
|
+
* @remarks
|
327
|
+
* [API](https://react-hook-form.com/api/usecontroller) • [Demo](https://codesandbox.io/s/usecontroller-0o8px)
|
328
|
+
*
|
329
|
+
* @param props - the path name to the form field value, and validation rules.
|
330
|
+
*
|
331
|
+
* @returns field properties, field and form state. {@link UseControllerReturn}
|
332
|
+
*
|
333
|
+
* @example
|
334
|
+
* ```tsx
|
335
|
+
* function Input(props) {
|
336
|
+
* const { field, fieldState, formState } = useController(props);
|
337
|
+
* return (
|
338
|
+
* <div>
|
339
|
+
* <input {...field} placeholder={props.name} />
|
340
|
+
* <p>{fieldState.isTouched && "Touched"}</p>
|
341
|
+
* <p>{formState.isSubmitted ? "submitted" : ""}</p>
|
342
|
+
* </div>
|
343
|
+
* );
|
344
|
+
* }
|
345
|
+
* ```
|
346
|
+
*/
|
216
347
|
function useController(props) {
|
217
348
|
const methods = useFormContext();
|
218
349
|
const { name, control = methods.control, shouldUnregister } = props;
|
@@ -221,13 +352,16 @@ function useController(props) {
|
|
221
352
|
control,
|
222
353
|
name,
|
223
354
|
defaultValue: get(control._formValues, name, get(control._defaultValues, name, props.defaultValue)),
|
224
|
-
exact:
|
355
|
+
exact: true,
|
225
356
|
});
|
226
357
|
const formState = useFormState({
|
227
358
|
control,
|
228
359
|
name,
|
229
360
|
});
|
230
|
-
const _registerProps = React.useRef(control.register(name,
|
361
|
+
const _registerProps = React.useRef(control.register(name, {
|
362
|
+
...props.rules,
|
363
|
+
value: value,
|
364
|
+
}));
|
231
365
|
React.useEffect(() => {
|
232
366
|
const updateMounted = (name, value) => {
|
233
367
|
const field = get(control._fields, name);
|
@@ -283,10 +417,59 @@ function useController(props) {
|
|
283
417
|
};
|
284
418
|
}
|
285
419
|
|
420
|
+
/**
|
421
|
+
* Component based on `useController` hook to work with controlled component.
|
422
|
+
*
|
423
|
+
* @remarks
|
424
|
+
* [API](https://react-hook-form.com/api/usecontroller/controller) • [Demo](https://codesandbox.io/s/react-hook-form-v6-controller-ts-jwyzw) • [Video](https://www.youtube.com/watch?v=N2UNk_UCVyA)
|
425
|
+
*
|
426
|
+
* @param props - the path name to the form field value, and validation rules.
|
427
|
+
*
|
428
|
+
* @returns provide field handler functions, field and form state.
|
429
|
+
*
|
430
|
+
* @example
|
431
|
+
* ```tsx
|
432
|
+
* function App() {
|
433
|
+
* const { control } = useForm<FormValues>({
|
434
|
+
* defaultValues: {
|
435
|
+
* test: ""
|
436
|
+
* }
|
437
|
+
* });
|
438
|
+
*
|
439
|
+
* return (
|
440
|
+
* <form>
|
441
|
+
* <Controller
|
442
|
+
* control={control}
|
443
|
+
* name="test"
|
444
|
+
* render={({ field: { onChange, onBlur, value, ref }, formState, fieldState }) => (
|
445
|
+
* <>
|
446
|
+
* <input
|
447
|
+
* onChange={onChange} // send value to hook form
|
448
|
+
* onBlur={onBlur} // notify when input is touched
|
449
|
+
* value={value} // return updated value
|
450
|
+
* ref={ref} // set ref for focus management
|
451
|
+
* />
|
452
|
+
* <p>{formState.isSubmitted ? "submitted" : ""}</p>
|
453
|
+
* <p>{fieldState.isTouched ? "touched" : ""}</p>
|
454
|
+
* </>
|
455
|
+
* )}
|
456
|
+
* />
|
457
|
+
* </form>
|
458
|
+
* );
|
459
|
+
* }
|
460
|
+
* ```
|
461
|
+
*/
|
286
462
|
const Controller = (props) => props.render(useController(props));
|
287
463
|
|
288
464
|
var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => validateAllFieldCriteria
|
289
|
-
?
|
465
|
+
? {
|
466
|
+
...errors[name],
|
467
|
+
types: {
|
468
|
+
...(errors[name] && errors[name].types ? errors[name].types : {}),
|
469
|
+
[type]: message || true,
|
470
|
+
},
|
471
|
+
}
|
472
|
+
: {};
|
290
473
|
|
291
474
|
/**
|
292
475
|
* Function for joining two paths / path strings to a {@link TypedFieldPath}.
|
@@ -350,8 +533,7 @@ const focusFieldBy = (fields, callback, fieldsNames) => {
|
|
350
533
|
for (const key of fieldsNames || Object.keys(fields)) {
|
351
534
|
const field = get(fields, key);
|
352
535
|
if (field) {
|
353
|
-
const _f = field
|
354
|
-
const current = omit(field, '_f');
|
536
|
+
const { _f, ...currentField } = field;
|
355
537
|
if (_f && callback(_f.name)) {
|
356
538
|
if (_f.ref.focus && isUndefined(_f.ref.focus())) {
|
357
539
|
break;
|
@@ -361,8 +543,8 @@ const focusFieldBy = (fields, callback, fieldsNames) => {
|
|
361
543
|
break;
|
362
544
|
}
|
363
545
|
}
|
364
|
-
else if (isObject(
|
365
|
-
focusFieldBy(
|
546
|
+
else if (isObject(currentField)) {
|
547
|
+
focusFieldBy(currentField, callback);
|
366
548
|
}
|
367
549
|
}
|
368
550
|
}
|
@@ -387,6 +569,241 @@ var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
|
|
387
569
|
[..._names.watch].some((watchName) => name.startsWith(watchName) &&
|
388
570
|
/^\.\w+/.test(name.slice(watchName.length))));
|
389
571
|
|
572
|
+
var updateFieldArrayRootError = (errors, error, name) => {
|
573
|
+
const fieldArrayErrors = compact(get(errors, name));
|
574
|
+
set(fieldArrayErrors, 'root', error[name]);
|
575
|
+
set(errors, name, fieldArrayErrors);
|
576
|
+
return errors;
|
577
|
+
};
|
578
|
+
|
579
|
+
var isBoolean = (value) => typeof value === 'boolean';
|
580
|
+
|
581
|
+
var isFileInput = (element) => element.type === 'file';
|
582
|
+
|
583
|
+
var isMessage = (value) => isString(value) || React.isValidElement(value);
|
584
|
+
|
585
|
+
var isRadioInput = (element) => element.type === 'radio';
|
586
|
+
|
587
|
+
var isRegex = (value) => value instanceof RegExp;
|
588
|
+
|
589
|
+
const defaultResult = {
|
590
|
+
value: false,
|
591
|
+
isValid: false,
|
592
|
+
};
|
593
|
+
const validResult = { value: true, isValid: true };
|
594
|
+
var getCheckboxValue = (options) => {
|
595
|
+
if (Array.isArray(options)) {
|
596
|
+
if (options.length > 1) {
|
597
|
+
const values = options
|
598
|
+
.filter((option) => option && option.checked && !option.disabled)
|
599
|
+
.map((option) => option.value);
|
600
|
+
return { value: values, isValid: !!values.length };
|
601
|
+
}
|
602
|
+
return options[0].checked && !options[0].disabled
|
603
|
+
? // @ts-expect-error expected to work in the browser
|
604
|
+
options[0].attributes && !isUndefined(options[0].attributes.value)
|
605
|
+
? isUndefined(options[0].value) || options[0].value === ''
|
606
|
+
? validResult
|
607
|
+
: { value: options[0].value, isValid: true }
|
608
|
+
: validResult
|
609
|
+
: defaultResult;
|
610
|
+
}
|
611
|
+
return defaultResult;
|
612
|
+
};
|
613
|
+
|
614
|
+
const defaultReturn = {
|
615
|
+
isValid: false,
|
616
|
+
value: null,
|
617
|
+
};
|
618
|
+
var getRadioValue = (options) => Array.isArray(options)
|
619
|
+
? options.reduce((previous, option) => option && option.checked && !option.disabled
|
620
|
+
? {
|
621
|
+
isValid: true,
|
622
|
+
value: option.value,
|
623
|
+
}
|
624
|
+
: previous, defaultReturn)
|
625
|
+
: defaultReturn;
|
626
|
+
|
627
|
+
function getValidateError(result, ref, type = 'validate') {
|
628
|
+
if (isMessage(result) ||
|
629
|
+
(Array.isArray(result) && result.every(isMessage)) ||
|
630
|
+
(isBoolean(result) && !result)) {
|
631
|
+
return {
|
632
|
+
type,
|
633
|
+
message: isMessage(result) ? result : '',
|
634
|
+
ref,
|
635
|
+
};
|
636
|
+
}
|
637
|
+
}
|
638
|
+
|
639
|
+
var getValueAndMessage = (validationData) => isObject(validationData) && !isRegex(validationData)
|
640
|
+
? validationData
|
641
|
+
: {
|
642
|
+
value: validationData,
|
643
|
+
message: '',
|
644
|
+
};
|
645
|
+
|
646
|
+
var validateField = async (field, inputValue, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
|
647
|
+
const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, disabled, } = field._f;
|
648
|
+
if (!mount || disabled) {
|
649
|
+
return {};
|
650
|
+
}
|
651
|
+
const inputRef = refs ? refs[0] : ref;
|
652
|
+
const setCustomValidity = (message) => {
|
653
|
+
if (shouldUseNativeValidation && inputRef.reportValidity) {
|
654
|
+
inputRef.setCustomValidity(isBoolean(message) ? '' : message || ' ');
|
655
|
+
inputRef.reportValidity();
|
656
|
+
}
|
657
|
+
};
|
658
|
+
const error = {};
|
659
|
+
const isRadio = isRadioInput(ref);
|
660
|
+
const isCheckBox = isCheckBoxInput(ref);
|
661
|
+
const isRadioOrCheckbox = isRadio || isCheckBox;
|
662
|
+
const isEmpty = ((valueAsNumber || isFileInput(ref)) && !ref.value) ||
|
663
|
+
inputValue === '' ||
|
664
|
+
(Array.isArray(inputValue) && !inputValue.length);
|
665
|
+
const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
|
666
|
+
const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
|
667
|
+
const message = exceedMax ? maxLengthMessage : minLengthMessage;
|
668
|
+
error[name] = {
|
669
|
+
type: exceedMax ? maxType : minType,
|
670
|
+
message,
|
671
|
+
ref,
|
672
|
+
...appendErrorsCurry(exceedMax ? maxType : minType, message),
|
673
|
+
};
|
674
|
+
};
|
675
|
+
if (required &&
|
676
|
+
((!isRadioOrCheckbox && (isEmpty || isNullOrUndefined(inputValue))) ||
|
677
|
+
(isBoolean(inputValue) && !inputValue) ||
|
678
|
+
(isCheckBox && !getCheckboxValue(refs).isValid) ||
|
679
|
+
(isRadio && !getRadioValue(refs).isValid))) {
|
680
|
+
const { value, message } = isMessage(required)
|
681
|
+
? { value: !!required, message: required }
|
682
|
+
: getValueAndMessage(required);
|
683
|
+
if (value) {
|
684
|
+
error[name] = {
|
685
|
+
type: INPUT_VALIDATION_RULES.required,
|
686
|
+
message,
|
687
|
+
ref: inputRef,
|
688
|
+
...appendErrorsCurry(INPUT_VALIDATION_RULES.required, message),
|
689
|
+
};
|
690
|
+
if (!validateAllFieldCriteria) {
|
691
|
+
setCustomValidity(message);
|
692
|
+
return error;
|
693
|
+
}
|
694
|
+
}
|
695
|
+
}
|
696
|
+
if (!isEmpty && (!isNullOrUndefined(min) || !isNullOrUndefined(max))) {
|
697
|
+
let exceedMax;
|
698
|
+
let exceedMin;
|
699
|
+
const maxOutput = getValueAndMessage(max);
|
700
|
+
const minOutput = getValueAndMessage(min);
|
701
|
+
if (!isNaN(inputValue)) {
|
702
|
+
const valueNumber = ref.valueAsNumber || +inputValue;
|
703
|
+
if (!isNullOrUndefined(maxOutput.value)) {
|
704
|
+
exceedMax = valueNumber > maxOutput.value;
|
705
|
+
}
|
706
|
+
if (!isNullOrUndefined(minOutput.value)) {
|
707
|
+
exceedMin = valueNumber < minOutput.value;
|
708
|
+
}
|
709
|
+
}
|
710
|
+
else {
|
711
|
+
const valueDate = ref.valueAsDate || new Date(inputValue);
|
712
|
+
if (isString(maxOutput.value)) {
|
713
|
+
exceedMax = valueDate > new Date(maxOutput.value);
|
714
|
+
}
|
715
|
+
if (isString(minOutput.value)) {
|
716
|
+
exceedMin = valueDate < new Date(minOutput.value);
|
717
|
+
}
|
718
|
+
}
|
719
|
+
if (exceedMax || exceedMin) {
|
720
|
+
getMinMaxMessage(!!exceedMax, maxOutput.message, minOutput.message, INPUT_VALIDATION_RULES.max, INPUT_VALIDATION_RULES.min);
|
721
|
+
if (!validateAllFieldCriteria) {
|
722
|
+
setCustomValidity(error[name].message);
|
723
|
+
return error;
|
724
|
+
}
|
725
|
+
}
|
726
|
+
}
|
727
|
+
if ((maxLength || minLength) &&
|
728
|
+
((!isEmpty && isString(inputValue)) ||
|
729
|
+
(isFieldArray && Array.isArray(inputValue)))) {
|
730
|
+
const maxLengthOutput = getValueAndMessage(maxLength);
|
731
|
+
const minLengthOutput = getValueAndMessage(minLength);
|
732
|
+
const exceedMax = !isNullOrUndefined(maxLengthOutput.value) &&
|
733
|
+
inputValue.length > maxLengthOutput.value;
|
734
|
+
const exceedMin = !isNullOrUndefined(minLengthOutput.value) &&
|
735
|
+
inputValue.length < minLengthOutput.value;
|
736
|
+
if (exceedMax || exceedMin) {
|
737
|
+
getMinMaxMessage(exceedMax, maxLengthOutput.message, minLengthOutput.message);
|
738
|
+
if (!validateAllFieldCriteria) {
|
739
|
+
setCustomValidity(error[name].message);
|
740
|
+
return error;
|
741
|
+
}
|
742
|
+
}
|
743
|
+
}
|
744
|
+
if (pattern && !isEmpty && isString(inputValue)) {
|
745
|
+
const { value: patternValue, message } = getValueAndMessage(pattern);
|
746
|
+
if (isRegex(patternValue) && !inputValue.match(patternValue)) {
|
747
|
+
error[name] = {
|
748
|
+
type: INPUT_VALIDATION_RULES.pattern,
|
749
|
+
message,
|
750
|
+
ref,
|
751
|
+
...appendErrorsCurry(INPUT_VALIDATION_RULES.pattern, message),
|
752
|
+
};
|
753
|
+
if (!validateAllFieldCriteria) {
|
754
|
+
setCustomValidity(message);
|
755
|
+
return error;
|
756
|
+
}
|
757
|
+
}
|
758
|
+
}
|
759
|
+
if (validate) {
|
760
|
+
if (isFunction(validate)) {
|
761
|
+
const result = await validate(inputValue);
|
762
|
+
const validateError = getValidateError(result, inputRef);
|
763
|
+
if (validateError) {
|
764
|
+
error[name] = {
|
765
|
+
...validateError,
|
766
|
+
...appendErrorsCurry(INPUT_VALIDATION_RULES.validate, validateError.message),
|
767
|
+
};
|
768
|
+
if (!validateAllFieldCriteria) {
|
769
|
+
setCustomValidity(validateError.message);
|
770
|
+
return error;
|
771
|
+
}
|
772
|
+
}
|
773
|
+
}
|
774
|
+
else if (isObject(validate)) {
|
775
|
+
let validationResult = {};
|
776
|
+
for (const key in validate) {
|
777
|
+
if (!isEmptyObject(validationResult) && !validateAllFieldCriteria) {
|
778
|
+
break;
|
779
|
+
}
|
780
|
+
const validateError = getValidateError(await validate[key](inputValue), inputRef, key);
|
781
|
+
if (validateError) {
|
782
|
+
validationResult = {
|
783
|
+
...validateError,
|
784
|
+
...appendErrorsCurry(key, validateError.message),
|
785
|
+
};
|
786
|
+
setCustomValidity(validateError.message);
|
787
|
+
if (validateAllFieldCriteria) {
|
788
|
+
error[name] = validationResult;
|
789
|
+
}
|
790
|
+
}
|
791
|
+
}
|
792
|
+
if (!isEmptyObject(validationResult)) {
|
793
|
+
error[name] = {
|
794
|
+
ref: inputRef,
|
795
|
+
...validationResult,
|
796
|
+
};
|
797
|
+
if (!validateAllFieldCriteria) {
|
798
|
+
return error;
|
799
|
+
}
|
800
|
+
}
|
801
|
+
}
|
802
|
+
}
|
803
|
+
setCustomValidity(true);
|
804
|
+
return error;
|
805
|
+
};
|
806
|
+
|
390
807
|
function append(data, value) {
|
391
808
|
return [...data, ...convertToArrayPayload(value)];
|
392
809
|
}
|
@@ -418,6 +835,14 @@ function cloneObject(data) {
|
|
418
835
|
|
419
836
|
var fillEmptyArray = (value) => Array.isArray(value) ? value.map(() => undefined) : undefined;
|
420
837
|
|
838
|
+
var getValidationModes = (mode) => ({
|
839
|
+
isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
|
840
|
+
isOnBlur: mode === VALIDATION_MODE.onBlur,
|
841
|
+
isOnChange: mode === VALIDATION_MODE.onChange,
|
842
|
+
isOnAll: mode === VALIDATION_MODE.all,
|
843
|
+
isOnTouch: mode === VALIDATION_MODE.onTouched,
|
844
|
+
});
|
845
|
+
|
421
846
|
function insert(data, index, value) {
|
422
847
|
return [
|
423
848
|
...data.slice(0, index),
|
@@ -463,7 +888,44 @@ var updateAt = (fieldValues, index, value) => {
|
|
463
888
|
return fieldValues;
|
464
889
|
};
|
465
890
|
|
466
|
-
|
891
|
+
/**
|
892
|
+
* A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc.
|
893
|
+
*
|
894
|
+
* @remarks
|
895
|
+
* [API](https://react-hook-form.com/api/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
|
896
|
+
*
|
897
|
+
* @param props - useFieldArray props
|
898
|
+
*
|
899
|
+
* @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
|
900
|
+
*
|
901
|
+
* @example
|
902
|
+
* ```tsx
|
903
|
+
* function App() {
|
904
|
+
* const { register, control, handleSubmit, reset, trigger, setError } = useForm({
|
905
|
+
* defaultValues: {
|
906
|
+
* test: []
|
907
|
+
* }
|
908
|
+
* });
|
909
|
+
* const { fields, append } = useFieldArray({
|
910
|
+
* control,
|
911
|
+
* name: "test"
|
912
|
+
* });
|
913
|
+
*
|
914
|
+
* return (
|
915
|
+
* <form onSubmit={handleSubmit(data => console.log(data))}>
|
916
|
+
* {fields.map((item, index) => (
|
917
|
+
* <input key={item.id} {...register(`test.${index}.firstName`)} />
|
918
|
+
* ))}
|
919
|
+
* <button type="button" onClick={() => append({ firstName: "bill" })}>
|
920
|
+
* append
|
921
|
+
* </button>
|
922
|
+
* <input type="submit" />
|
923
|
+
* </form>
|
924
|
+
* );
|
925
|
+
* }
|
926
|
+
* ```
|
927
|
+
*/
|
928
|
+
function useFieldArray(props) {
|
467
929
|
const methods = useFormContext();
|
468
930
|
const { control = methods.control, name, shouldUnregister } = props;
|
469
931
|
const [fields, setFields] = React.useState(control._getFieldArray(name));
|
@@ -474,6 +936,9 @@ const useFieldArray = (props) => {
|
|
474
936
|
_name.current = name;
|
475
937
|
_fieldIds.current = fields;
|
476
938
|
control._names.array.add(name);
|
939
|
+
if (props.rules) {
|
940
|
+
control.register(name, props.rules);
|
941
|
+
}
|
477
942
|
const callback = React.useCallback(({ values, name: fieldArrayName }) => {
|
478
943
|
if (fieldArrayName === _name.current || !fieldArrayName) {
|
479
944
|
const fieldValues = get(values, _name.current, []);
|
@@ -555,13 +1020,14 @@ const useFieldArray = (props) => {
|
|
555
1020
|
}, false);
|
556
1021
|
};
|
557
1022
|
const update = (index, value) => {
|
1023
|
+
const updateValue = cloneObject(value);
|
558
1024
|
const updatedFieldArrayValues = updateAt(control._getFieldArray(name), index, value);
|
559
1025
|
ids.current = [...updatedFieldArrayValues].map((item, i) => !item || i === index ? generateId() : ids.current[i]);
|
560
1026
|
updateValues(updatedFieldArrayValues);
|
561
1027
|
setFields([...updatedFieldArrayValues]);
|
562
1028
|
control._updateFieldArray(name, updatedFieldArrayValues, updateAt, {
|
563
1029
|
argA: index,
|
564
|
-
argB:
|
1030
|
+
argB: updateValue,
|
565
1031
|
}, true, false);
|
566
1032
|
};
|
567
1033
|
const replace = (value) => {
|
@@ -575,15 +1041,33 @@ const useFieldArray = (props) => {
|
|
575
1041
|
control._stateFlags.action = false;
|
576
1042
|
isWatched(name, control._names) && control._subjects.state.next({});
|
577
1043
|
if (_actioned.current) {
|
578
|
-
control.
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
1044
|
+
if (control._options.resolver) {
|
1045
|
+
control._executeSchema([name]).then((result) => {
|
1046
|
+
const error = get(result.errors, name);
|
1047
|
+
if (error && error.type && !get(control._formState.errors, name)) {
|
1048
|
+
set(control._formState.errors, name, error);
|
1049
|
+
control._subjects.state.next({
|
1050
|
+
errors: control._formState.errors,
|
1051
|
+
});
|
1052
|
+
}
|
1053
|
+
});
|
1054
|
+
}
|
1055
|
+
else {
|
1056
|
+
const field = get(control._fields, name);
|
1057
|
+
const validationModeBeforeSubmit = getValidationModes(control._options.mode);
|
1058
|
+
if ((!validationModeBeforeSubmit.isOnSubmit ||
|
1059
|
+
control._formState.isSubmitted) &&
|
1060
|
+
field &&
|
1061
|
+
field._f) {
|
1062
|
+
validateField(field, get(control._formValues, name), control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => {
|
1063
|
+
if (!isEmptyObject(error)) {
|
1064
|
+
control._subjects.state.next({
|
1065
|
+
errors: updateFieldArrayRootError(control._formState.errors, error, name),
|
1066
|
+
});
|
1067
|
+
}
|
584
1068
|
});
|
585
1069
|
}
|
586
|
-
}
|
1070
|
+
}
|
587
1071
|
}
|
588
1072
|
control._subjects.watch.next({
|
589
1073
|
name,
|
@@ -610,9 +1094,12 @@ const useFieldArray = (props) => {
|
|
610
1094
|
insert: React.useCallback(insert$1, [updateValues, name, control]),
|
611
1095
|
update: React.useCallback(update, [updateValues, name, control]),
|
612
1096
|
replace: React.useCallback(replace, [updateValues, name, control]),
|
613
|
-
fields: React.useMemo(() => fields.map((field, index) => (
|
1097
|
+
fields: React.useMemo(() => fields.map((field, index) => ({
|
1098
|
+
...field,
|
1099
|
+
id: ids.current[index] || generateId(),
|
1100
|
+
})), [fields]),
|
614
1101
|
};
|
615
|
-
}
|
1102
|
+
}
|
616
1103
|
|
617
1104
|
function createSubject() {
|
618
1105
|
let _observers = [];
|
@@ -675,24 +1162,10 @@ function deepEqual(object1, object2) {
|
|
675
1162
|
return true;
|
676
1163
|
}
|
677
1164
|
|
678
|
-
var getValidationModes = (mode) => ({
|
679
|
-
isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
|
680
|
-
isOnBlur: mode === VALIDATION_MODE.onBlur,
|
681
|
-
isOnChange: mode === VALIDATION_MODE.onChange,
|
682
|
-
isOnAll: mode === VALIDATION_MODE.all,
|
683
|
-
isOnTouch: mode === VALIDATION_MODE.onTouched,
|
684
|
-
});
|
685
|
-
|
686
|
-
var isBoolean = (value) => typeof value === 'boolean';
|
687
|
-
|
688
|
-
var isFileInput = (element) => element.type === 'file';
|
689
|
-
|
690
1165
|
var isHTMLElement = (value) => value instanceof HTMLElement;
|
691
1166
|
|
692
1167
|
var isMultipleSelect = (element) => element.type === `select-multiple`;
|
693
1168
|
|
694
|
-
var isRadioInput = (element) => element.type === 'radio';
|
695
|
-
|
696
1169
|
var isRadioOrCheckbox = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
|
697
1170
|
|
698
1171
|
var isWeb = typeof window !== 'undefined' &&
|
@@ -709,6 +1182,14 @@ function baseGet(object, updatePath) {
|
|
709
1182
|
}
|
710
1183
|
return object;
|
711
1184
|
}
|
1185
|
+
function isEmptyArray(obj) {
|
1186
|
+
for (const key in obj) {
|
1187
|
+
if (!isUndefined(obj[key])) {
|
1188
|
+
return false;
|
1189
|
+
}
|
1190
|
+
}
|
1191
|
+
return true;
|
1192
|
+
}
|
712
1193
|
function unset(object, path) {
|
713
1194
|
const updatePath = isKey(path) ? [path] : stringToPath(path);
|
714
1195
|
const childObject = updatePath.length == 1 ? object : baseGet(object, updatePath);
|
@@ -730,8 +1211,7 @@ function unset(object, path) {
|
|
730
1211
|
objectRef = objectRef ? objectRef[item] : object[item];
|
731
1212
|
if (currentPathsLength === index &&
|
732
1213
|
((isObject(objectRef) && isEmptyObject(objectRef)) ||
|
733
|
-
(Array.isArray(objectRef) &&
|
734
|
-
!objectRef.filter((data) => (isObject(data) && !isEmptyObject(data)) || isBoolean(data)).length))) {
|
1214
|
+
(Array.isArray(objectRef) && isEmptyArray(objectRef)))) {
|
735
1215
|
previousObjRef ? delete previousObjRef[item] : delete object[item];
|
736
1216
|
}
|
737
1217
|
previousObjRef = objectRef;
|
@@ -766,7 +1246,7 @@ function getDirtyFieldsFromDefaultValues(data, formValues, dirtyFieldsFromValues
|
|
766
1246
|
isPrimitive(dirtyFieldsFromValues[key])) {
|
767
1247
|
dirtyFieldsFromValues[key] = Array.isArray(data[key])
|
768
1248
|
? markFieldsDirty(data[key], [])
|
769
|
-
:
|
1249
|
+
: { ...markFieldsDirty(data[key]) };
|
770
1250
|
}
|
771
1251
|
else {
|
772
1252
|
getDirtyFieldsFromDefaultValues(data[key], isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
|
@@ -781,31 +1261,6 @@ function getDirtyFieldsFromDefaultValues(data, formValues, dirtyFieldsFromValues
|
|
781
1261
|
}
|
782
1262
|
var getDirtyFields = (defaultValues, formValues) => getDirtyFieldsFromDefaultValues(defaultValues, formValues, markFieldsDirty(formValues));
|
783
1263
|
|
784
|
-
const defaultResult = {
|
785
|
-
value: false,
|
786
|
-
isValid: false,
|
787
|
-
};
|
788
|
-
const validResult = { value: true, isValid: true };
|
789
|
-
var getCheckboxValue = (options) => {
|
790
|
-
if (Array.isArray(options)) {
|
791
|
-
if (options.length > 1) {
|
792
|
-
const values = options
|
793
|
-
.filter((option) => option && option.checked && !option.disabled)
|
794
|
-
.map((option) => option.value);
|
795
|
-
return { value: values, isValid: !!values.length };
|
796
|
-
}
|
797
|
-
return options[0].checked && !options[0].disabled
|
798
|
-
? // @ts-expect-error expected to work in the browser
|
799
|
-
options[0].attributes && !isUndefined(options[0].attributes.value)
|
800
|
-
? isUndefined(options[0].value) || options[0].value === ''
|
801
|
-
? validResult
|
802
|
-
: { value: options[0].value, isValid: true }
|
803
|
-
: validResult
|
804
|
-
: defaultResult;
|
805
|
-
}
|
806
|
-
return defaultResult;
|
807
|
-
};
|
808
|
-
|
809
1264
|
var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isUndefined(value)
|
810
1265
|
? value
|
811
1266
|
: valueAsNumber
|
@@ -818,19 +1273,6 @@ var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isU
|
|
818
1273
|
? setValueAs(value)
|
819
1274
|
: value;
|
820
1275
|
|
821
|
-
const defaultReturn = {
|
822
|
-
isValid: false,
|
823
|
-
value: null,
|
824
|
-
};
|
825
|
-
var getRadioValue = (options) => Array.isArray(options)
|
826
|
-
? options.reduce((previous, option) => option && option.checked && !option.disabled
|
827
|
-
? {
|
828
|
-
isValid: true,
|
829
|
-
value: option.value,
|
830
|
-
}
|
831
|
-
: previous, defaultReturn)
|
832
|
-
: defaultReturn;
|
833
|
-
|
834
1276
|
function getFieldValue(_f) {
|
835
1277
|
const ref = _f.ref;
|
836
1278
|
if (_f.refs ? _f.refs.every((ref) => ref.disabled) : ref.disabled) {
|
@@ -865,8 +1307,6 @@ var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeVal
|
|
865
1307
|
};
|
866
1308
|
};
|
867
1309
|
|
868
|
-
var isRegex = (value) => value instanceof RegExp;
|
869
|
-
|
870
1310
|
var getRuleValue = (rule) => isUndefined(rule)
|
871
1311
|
? undefined
|
872
1312
|
: isRegex(rule)
|
@@ -933,171 +1373,16 @@ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode)
|
|
933
1373
|
|
934
1374
|
var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
|
935
1375
|
|
936
|
-
var isMessage = (value) => isString(value) || React.isValidElement(value);
|
937
|
-
|
938
|
-
function getValidateError(result, ref, type = 'validate') {
|
939
|
-
if (isMessage(result) ||
|
940
|
-
(Array.isArray(result) && result.every(isMessage)) ||
|
941
|
-
(isBoolean(result) && !result)) {
|
942
|
-
return {
|
943
|
-
type,
|
944
|
-
message: isMessage(result) ? result : '',
|
945
|
-
ref,
|
946
|
-
};
|
947
|
-
}
|
948
|
-
}
|
949
|
-
|
950
|
-
var getValueAndMessage = (validationData) => isObject(validationData) && !isRegex(validationData)
|
951
|
-
? validationData
|
952
|
-
: {
|
953
|
-
value: validationData,
|
954
|
-
message: '',
|
955
|
-
};
|
956
|
-
|
957
|
-
var validateField = async (field, inputValue, validateAllFieldCriteria, shouldUseNativeValidation) => {
|
958
|
-
const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, disabled, } = field._f;
|
959
|
-
if (!mount || disabled) {
|
960
|
-
return {};
|
961
|
-
}
|
962
|
-
const inputRef = refs ? refs[0] : ref;
|
963
|
-
const setCustomValidity = (message) => {
|
964
|
-
if (shouldUseNativeValidation && inputRef.reportValidity) {
|
965
|
-
inputRef.setCustomValidity(isBoolean(message) ? '' : message || ' ');
|
966
|
-
inputRef.reportValidity();
|
967
|
-
}
|
968
|
-
};
|
969
|
-
const error = {};
|
970
|
-
const isRadio = isRadioInput(ref);
|
971
|
-
const isCheckBox = isCheckBoxInput(ref);
|
972
|
-
const isRadioOrCheckbox = isRadio || isCheckBox;
|
973
|
-
const isEmpty = ((valueAsNumber || isFileInput(ref)) && !ref.value) ||
|
974
|
-
inputValue === '' ||
|
975
|
-
(Array.isArray(inputValue) && !inputValue.length);
|
976
|
-
const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
|
977
|
-
const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
|
978
|
-
const message = exceedMax ? maxLengthMessage : minLengthMessage;
|
979
|
-
error[name] = Object.assign({ type: exceedMax ? maxType : minType, message,
|
980
|
-
ref }, appendErrorsCurry(exceedMax ? maxType : minType, message));
|
981
|
-
};
|
982
|
-
if (required &&
|
983
|
-
((!isRadioOrCheckbox && (isEmpty || isNullOrUndefined(inputValue))) ||
|
984
|
-
(isBoolean(inputValue) && !inputValue) ||
|
985
|
-
(isCheckBox && !getCheckboxValue(refs).isValid) ||
|
986
|
-
(isRadio && !getRadioValue(refs).isValid))) {
|
987
|
-
const { value, message } = isMessage(required)
|
988
|
-
? { value: !!required, message: required }
|
989
|
-
: getValueAndMessage(required);
|
990
|
-
if (value) {
|
991
|
-
error[name] = Object.assign({ type: INPUT_VALIDATION_RULES.required, message, ref: inputRef }, appendErrorsCurry(INPUT_VALIDATION_RULES.required, message));
|
992
|
-
if (!validateAllFieldCriteria) {
|
993
|
-
setCustomValidity(message);
|
994
|
-
return error;
|
995
|
-
}
|
996
|
-
}
|
997
|
-
}
|
998
|
-
if (!isEmpty && (!isNullOrUndefined(min) || !isNullOrUndefined(max))) {
|
999
|
-
let exceedMax;
|
1000
|
-
let exceedMin;
|
1001
|
-
const maxOutput = getValueAndMessage(max);
|
1002
|
-
const minOutput = getValueAndMessage(min);
|
1003
|
-
if (!isNaN(inputValue)) {
|
1004
|
-
const valueNumber = ref.valueAsNumber || +inputValue;
|
1005
|
-
if (!isNullOrUndefined(maxOutput.value)) {
|
1006
|
-
exceedMax = valueNumber > maxOutput.value;
|
1007
|
-
}
|
1008
|
-
if (!isNullOrUndefined(minOutput.value)) {
|
1009
|
-
exceedMin = valueNumber < minOutput.value;
|
1010
|
-
}
|
1011
|
-
}
|
1012
|
-
else {
|
1013
|
-
const valueDate = ref.valueAsDate || new Date(inputValue);
|
1014
|
-
if (isString(maxOutput.value)) {
|
1015
|
-
exceedMax = valueDate > new Date(maxOutput.value);
|
1016
|
-
}
|
1017
|
-
if (isString(minOutput.value)) {
|
1018
|
-
exceedMin = valueDate < new Date(minOutput.value);
|
1019
|
-
}
|
1020
|
-
}
|
1021
|
-
if (exceedMax || exceedMin) {
|
1022
|
-
getMinMaxMessage(!!exceedMax, maxOutput.message, minOutput.message, INPUT_VALIDATION_RULES.max, INPUT_VALIDATION_RULES.min);
|
1023
|
-
if (!validateAllFieldCriteria) {
|
1024
|
-
setCustomValidity(error[name].message);
|
1025
|
-
return error;
|
1026
|
-
}
|
1027
|
-
}
|
1028
|
-
}
|
1029
|
-
if ((maxLength || minLength) && !isEmpty && isString(inputValue)) {
|
1030
|
-
const maxLengthOutput = getValueAndMessage(maxLength);
|
1031
|
-
const minLengthOutput = getValueAndMessage(minLength);
|
1032
|
-
const exceedMax = !isNullOrUndefined(maxLengthOutput.value) &&
|
1033
|
-
inputValue.length > maxLengthOutput.value;
|
1034
|
-
const exceedMin = !isNullOrUndefined(minLengthOutput.value) &&
|
1035
|
-
inputValue.length < minLengthOutput.value;
|
1036
|
-
if (exceedMax || exceedMin) {
|
1037
|
-
getMinMaxMessage(exceedMax, maxLengthOutput.message, minLengthOutput.message);
|
1038
|
-
if (!validateAllFieldCriteria) {
|
1039
|
-
setCustomValidity(error[name].message);
|
1040
|
-
return error;
|
1041
|
-
}
|
1042
|
-
}
|
1043
|
-
}
|
1044
|
-
if (pattern && !isEmpty && isString(inputValue)) {
|
1045
|
-
const { value: patternValue, message } = getValueAndMessage(pattern);
|
1046
|
-
if (isRegex(patternValue) && !inputValue.match(patternValue)) {
|
1047
|
-
error[name] = Object.assign({ type: INPUT_VALIDATION_RULES.pattern, message,
|
1048
|
-
ref }, appendErrorsCurry(INPUT_VALIDATION_RULES.pattern, message));
|
1049
|
-
if (!validateAllFieldCriteria) {
|
1050
|
-
setCustomValidity(message);
|
1051
|
-
return error;
|
1052
|
-
}
|
1053
|
-
}
|
1054
|
-
}
|
1055
|
-
if (validate) {
|
1056
|
-
if (isFunction(validate)) {
|
1057
|
-
const result = await validate(inputValue);
|
1058
|
-
const validateError = getValidateError(result, inputRef);
|
1059
|
-
if (validateError) {
|
1060
|
-
error[name] = Object.assign(Object.assign({}, validateError), appendErrorsCurry(INPUT_VALIDATION_RULES.validate, validateError.message));
|
1061
|
-
if (!validateAllFieldCriteria) {
|
1062
|
-
setCustomValidity(validateError.message);
|
1063
|
-
return error;
|
1064
|
-
}
|
1065
|
-
}
|
1066
|
-
}
|
1067
|
-
else if (isObject(validate)) {
|
1068
|
-
let validationResult = {};
|
1069
|
-
for (const key in validate) {
|
1070
|
-
if (!isEmptyObject(validationResult) && !validateAllFieldCriteria) {
|
1071
|
-
break;
|
1072
|
-
}
|
1073
|
-
const validateError = getValidateError(await validate[key](inputValue), inputRef, key);
|
1074
|
-
if (validateError) {
|
1075
|
-
validationResult = Object.assign(Object.assign({}, validateError), appendErrorsCurry(key, validateError.message));
|
1076
|
-
setCustomValidity(validateError.message);
|
1077
|
-
if (validateAllFieldCriteria) {
|
1078
|
-
error[name] = validationResult;
|
1079
|
-
}
|
1080
|
-
}
|
1081
|
-
}
|
1082
|
-
if (!isEmptyObject(validationResult)) {
|
1083
|
-
error[name] = Object.assign({ ref: inputRef }, validationResult);
|
1084
|
-
if (!validateAllFieldCriteria) {
|
1085
|
-
return error;
|
1086
|
-
}
|
1087
|
-
}
|
1088
|
-
}
|
1089
|
-
}
|
1090
|
-
setCustomValidity(true);
|
1091
|
-
return error;
|
1092
|
-
};
|
1093
|
-
|
1094
1376
|
const defaultOptions = {
|
1095
1377
|
mode: VALIDATION_MODE.onSubmit,
|
1096
1378
|
reValidateMode: VALIDATION_MODE.onChange,
|
1097
1379
|
shouldFocusError: true,
|
1098
1380
|
};
|
1099
1381
|
function createFormControl(props = {}) {
|
1100
|
-
let _options =
|
1382
|
+
let _options = {
|
1383
|
+
...defaultOptions,
|
1384
|
+
...props,
|
1385
|
+
};
|
1101
1386
|
let _formState = {
|
1102
1387
|
isDirty: false,
|
1103
1388
|
isValidating: false,
|
@@ -1111,7 +1396,7 @@ function createFormControl(props = {}) {
|
|
1111
1396
|
errors: {},
|
1112
1397
|
};
|
1113
1398
|
let _fields = {};
|
1114
|
-
let _defaultValues = _options.defaultValues || {};
|
1399
|
+
let _defaultValues = cloneObject(_options.defaultValues) || {};
|
1115
1400
|
let _formValues = _options.shouldUnregister
|
1116
1401
|
? {}
|
1117
1402
|
: cloneObject(_defaultValues);
|
@@ -1164,21 +1449,22 @@ function createFormControl(props = {}) {
|
|
1164
1449
|
}
|
1165
1450
|
return isValid;
|
1166
1451
|
};
|
1167
|
-
const _updateFieldArray = (name, values = [], method, args, shouldSetValues = true,
|
1452
|
+
const _updateFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
|
1168
1453
|
if (args && method) {
|
1169
1454
|
_stateFlags.action = true;
|
1170
|
-
if (
|
1455
|
+
if (shouldUpdateFieldsAndState && Array.isArray(get(_fields, name))) {
|
1171
1456
|
const fieldValues = method(get(_fields, name), args.argA, args.argB);
|
1172
1457
|
shouldSetValues && set(_fields, name, fieldValues);
|
1173
1458
|
}
|
1174
1459
|
if (_proxyFormState.errors &&
|
1175
|
-
|
1460
|
+
shouldUpdateFieldsAndState &&
|
1176
1461
|
Array.isArray(get(_formState.errors, name))) {
|
1177
1462
|
const errors = method(get(_formState.errors, name), args.argA, args.argB);
|
1178
1463
|
shouldSetValues && set(_formState.errors, name, errors);
|
1179
1464
|
unsetEmptyArray(_formState.errors, name);
|
1180
1465
|
}
|
1181
1466
|
if (_proxyFormState.touchedFields &&
|
1467
|
+
shouldUpdateFieldsAndState &&
|
1182
1468
|
Array.isArray(get(_formState.touchedFields, name))) {
|
1183
1469
|
const touchedFields = method(get(_formState.touchedFields, name), args.argA, args.argB);
|
1184
1470
|
shouldSetValues && set(_formState.touchedFields, name, touchedFields);
|
@@ -1264,12 +1550,21 @@ function createFormControl(props = {}) {
|
|
1264
1550
|
!isEmptyObject(fieldState) ||
|
1265
1551
|
shouldUpdateValid) &&
|
1266
1552
|
!shouldSkipRender) {
|
1267
|
-
const updatedFormState =
|
1268
|
-
|
1553
|
+
const updatedFormState = {
|
1554
|
+
...fieldState,
|
1555
|
+
...(shouldUpdateValid ? { isValid } : {}),
|
1556
|
+
errors: _formState.errors,
|
1557
|
+
name,
|
1558
|
+
};
|
1559
|
+
_formState = {
|
1560
|
+
..._formState,
|
1561
|
+
...updatedFormState,
|
1562
|
+
};
|
1269
1563
|
_subjects.state.next(updatedFormState);
|
1270
1564
|
}
|
1271
1565
|
validateFields[name]--;
|
1272
|
-
if (_proxyFormState.isValidating &&
|
1566
|
+
if (_proxyFormState.isValidating &&
|
1567
|
+
!Object.values(validateFields).some((v) => v)) {
|
1273
1568
|
_subjects.state.next({
|
1274
1569
|
isValidating: false,
|
1275
1570
|
});
|
@@ -1277,7 +1572,7 @@ function createFormControl(props = {}) {
|
|
1277
1572
|
}
|
1278
1573
|
};
|
1279
1574
|
const _executeSchema = async (name) => _options.resolver
|
1280
|
-
? await _options.resolver(
|
1575
|
+
? await _options.resolver({ ..._formValues }, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation))
|
1281
1576
|
: {};
|
1282
1577
|
const executeSchemaAndUpdateState = async (names) => {
|
1283
1578
|
const { errors } = await _executeSchema();
|
@@ -1300,20 +1595,28 @@ function createFormControl(props = {}) {
|
|
1300
1595
|
for (const name in fields) {
|
1301
1596
|
const field = fields[name];
|
1302
1597
|
if (field) {
|
1303
|
-
const
|
1304
|
-
|
1305
|
-
|
1306
|
-
const fieldError = await validateField(field, get(_formValues,
|
1307
|
-
if (fieldError[
|
1598
|
+
const { _f, ...fieldValue } = field;
|
1599
|
+
if (_f) {
|
1600
|
+
const isFieldArrayRoot = _names.array.has(_f.name);
|
1601
|
+
const fieldError = await validateField(field, get(_formValues, _f.name), shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation, isFieldArrayRoot);
|
1602
|
+
if (fieldError[_f.name]) {
|
1308
1603
|
context.valid = false;
|
1309
1604
|
if (shouldOnlyCheckValid) {
|
1310
1605
|
break;
|
1311
1606
|
}
|
1312
1607
|
}
|
1313
1608
|
if (!shouldOnlyCheckValid) {
|
1314
|
-
fieldError
|
1315
|
-
|
1316
|
-
|
1609
|
+
if (get(fieldError, _f.name)) {
|
1610
|
+
if (isFieldArrayRoot) {
|
1611
|
+
updateFieldArrayRootError(_formState.errors, fieldError, _f.name);
|
1612
|
+
}
|
1613
|
+
else {
|
1614
|
+
set(_formState.errors, _f.name, fieldError[_f.name]);
|
1615
|
+
}
|
1616
|
+
}
|
1617
|
+
else {
|
1618
|
+
unset(_formState.errors, _f.name);
|
1619
|
+
}
|
1317
1620
|
}
|
1318
1621
|
}
|
1319
1622
|
fieldValue &&
|
@@ -1336,13 +1639,15 @@ function createFormControl(props = {}) {
|
|
1336
1639
|
const _getDirty = (name, data) => (name && data && set(_formValues, name, data),
|
1337
1640
|
!deepEqual(getValues(), _defaultValues));
|
1338
1641
|
const _getWatch = (names, defaultValue, isGlobal) => {
|
1339
|
-
const fieldValues =
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1642
|
+
const fieldValues = {
|
1643
|
+
...(_stateFlags.mount
|
1644
|
+
? _formValues
|
1645
|
+
: isUndefined(defaultValue)
|
1646
|
+
? _defaultValues
|
1647
|
+
: isString(names)
|
1648
|
+
? { [names]: defaultValue }
|
1649
|
+
: defaultValue),
|
1650
|
+
};
|
1346
1651
|
return generateWatchOutput(names, _names, fieldValues, isGlobal);
|
1347
1652
|
};
|
1348
1653
|
const _getFieldArray = (name) => compact(get(_stateFlags.mount ? _formValues : _defaultValues, name, props.shouldUnregister ? get(_defaultValues, name, []) : []));
|
@@ -1364,9 +1669,10 @@ function createFormControl(props = {}) {
|
|
1364
1669
|
else if (fieldReference.refs) {
|
1365
1670
|
if (isCheckBoxInput(fieldReference.ref)) {
|
1366
1671
|
fieldReference.refs.length > 1
|
1367
|
-
? fieldReference.refs.forEach((checkboxRef) =>
|
1368
|
-
|
1369
|
-
|
1672
|
+
? fieldReference.refs.forEach((checkboxRef) => !checkboxRef.disabled &&
|
1673
|
+
(checkboxRef.checked = Array.isArray(fieldValue)
|
1674
|
+
? !!fieldValue.find((data) => data === checkboxRef.value)
|
1675
|
+
: fieldValue === checkboxRef.value))
|
1370
1676
|
: fieldReference.refs[0] &&
|
1371
1677
|
(fieldReference.refs[0].checked = !!fieldValue);
|
1372
1678
|
}
|
@@ -1467,7 +1773,7 @@ function createFormControl(props = {}) {
|
|
1467
1773
|
});
|
1468
1774
|
if (shouldSkipValidation) {
|
1469
1775
|
return (shouldRender &&
|
1470
|
-
_subjects.state.next(
|
1776
|
+
_subjects.state.next({ name, ...(watched ? {} : fieldState) }));
|
1471
1777
|
}
|
1472
1778
|
!isBlurEvent && watched && _subjects.state.next({});
|
1473
1779
|
validateFields[name] = validateFields[name] ? +1 : 1;
|
@@ -1486,7 +1792,8 @@ function createFormControl(props = {}) {
|
|
1486
1792
|
error = (await validateField(field, get(_formValues, name), shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
|
1487
1793
|
isValid = await _updateValid(true);
|
1488
1794
|
}
|
1489
|
-
field._f.deps &&
|
1795
|
+
field._f.deps &&
|
1796
|
+
trigger(field._f.deps);
|
1490
1797
|
shouldRenderByError(false, name, isValid, error, fieldState);
|
1491
1798
|
}
|
1492
1799
|
};
|
@@ -1514,17 +1821,25 @@ function createFormControl(props = {}) {
|
|
1514
1821
|
else {
|
1515
1822
|
validationResult = isValid = await executeBuildInValidation(_fields);
|
1516
1823
|
}
|
1517
|
-
_subjects.state.next(
|
1518
|
-
(
|
1519
|
-
|
1520
|
-
|
1824
|
+
_subjects.state.next({
|
1825
|
+
...(!isString(name) ||
|
1826
|
+
(_proxyFormState.isValid && isValid !== _formState.isValid)
|
1827
|
+
? {}
|
1828
|
+
: { name }),
|
1829
|
+
...(_options.resolver ? { isValid } : {}),
|
1830
|
+
errors: _formState.errors,
|
1831
|
+
isValidating: false,
|
1832
|
+
});
|
1521
1833
|
options.shouldFocus &&
|
1522
1834
|
!validationResult &&
|
1523
1835
|
focusFieldBy(_fields, (key) => get(_formState.errors, key), name ? fieldNames : _names.mount);
|
1524
1836
|
return validationResult;
|
1525
1837
|
};
|
1526
1838
|
const getValues = (fieldNames) => {
|
1527
|
-
const values =
|
1839
|
+
const values = {
|
1840
|
+
..._defaultValues,
|
1841
|
+
...(_stateFlags.mount ? _formValues : {}),
|
1842
|
+
};
|
1528
1843
|
return isUndefined(fieldNames)
|
1529
1844
|
? values
|
1530
1845
|
: isString(fieldNames)
|
@@ -1547,7 +1862,10 @@ function createFormControl(props = {}) {
|
|
1547
1862
|
};
|
1548
1863
|
const setError = (name, error, options) => {
|
1549
1864
|
const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
|
1550
|
-
set(_formState.errors, name,
|
1865
|
+
set(_formState.errors, name, {
|
1866
|
+
...error,
|
1867
|
+
ref,
|
1868
|
+
});
|
1551
1869
|
_subjects.state.next({
|
1552
1870
|
name,
|
1553
1871
|
errors: _formState.errors,
|
@@ -1578,14 +1896,22 @@ function createFormControl(props = {}) {
|
|
1578
1896
|
}
|
1579
1897
|
}
|
1580
1898
|
_subjects.watch.next({});
|
1581
|
-
_subjects.state.next(
|
1899
|
+
_subjects.state.next({
|
1900
|
+
..._formState,
|
1901
|
+
...(!options.keepDirty ? {} : { isDirty: _getDirty() }),
|
1902
|
+
});
|
1582
1903
|
_proxyFormState.isValid && _updateValid();
|
1583
1904
|
};
|
1584
1905
|
const register = (name, options = {}) => {
|
1585
1906
|
let field = get(_fields, name);
|
1586
1907
|
const disabledIsDefined = isBoolean(options.disabled);
|
1587
1908
|
set(_fields, name, {
|
1588
|
-
_f:
|
1909
|
+
_f: {
|
1910
|
+
...(field && field._f ? field._f : { ref: { name } }),
|
1911
|
+
name,
|
1912
|
+
mount: true,
|
1913
|
+
...options,
|
1914
|
+
},
|
1589
1915
|
});
|
1590
1916
|
_names.mount.add(name);
|
1591
1917
|
field
|
@@ -1594,17 +1920,22 @@ function createFormControl(props = {}) {
|
|
1594
1920
|
? undefined
|
1595
1921
|
: get(_formValues, name, getFieldValue(field._f)))
|
1596
1922
|
: updateValidAndValue(name, true, options.value);
|
1597
|
-
return
|
1598
|
-
? {
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1923
|
+
return {
|
1924
|
+
...(disabledIsDefined ? { disabled: options.disabled } : {}),
|
1925
|
+
...(_options.shouldUseNativeValidation
|
1926
|
+
? {
|
1927
|
+
required: !!options.required,
|
1928
|
+
min: getRuleValue(options.min),
|
1929
|
+
max: getRuleValue(options.max),
|
1930
|
+
minLength: getRuleValue(options.minLength),
|
1931
|
+
maxLength: getRuleValue(options.maxLength),
|
1932
|
+
pattern: getRuleValue(options.pattern),
|
1933
|
+
}
|
1934
|
+
: {}),
|
1935
|
+
name,
|
1936
|
+
onChange,
|
1937
|
+
onBlur: onChange,
|
1938
|
+
ref: (ref) => {
|
1608
1939
|
if (ref) {
|
1609
1940
|
register(name, options);
|
1610
1941
|
field = get(_fields, name);
|
@@ -1621,12 +1952,15 @@ function createFormControl(props = {}) {
|
|
1621
1952
|
return;
|
1622
1953
|
}
|
1623
1954
|
set(_fields, name, {
|
1624
|
-
_f:
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1955
|
+
_f: {
|
1956
|
+
...field._f,
|
1957
|
+
...(radioOrCheckbox
|
1958
|
+
? {
|
1959
|
+
refs: [...refs.filter(live), fieldRef],
|
1960
|
+
ref: { type: fieldRef.type, name },
|
1961
|
+
}
|
1962
|
+
: { ref: fieldRef }),
|
1963
|
+
},
|
1630
1964
|
});
|
1631
1965
|
updateValidAndValue(name, false, undefined, fieldRef);
|
1632
1966
|
}
|
@@ -1639,7 +1973,8 @@ function createFormControl(props = {}) {
|
|
1639
1973
|
!(isNameInFieldArray(_names.array, name) && _stateFlags.action) &&
|
1640
1974
|
_names.unMount.add(name);
|
1641
1975
|
}
|
1642
|
-
}
|
1976
|
+
},
|
1977
|
+
};
|
1643
1978
|
};
|
1644
1979
|
const handleSubmit = (onValid, onInvalid) => async (e) => {
|
1645
1980
|
if (e) {
|
@@ -1647,9 +1982,7 @@ function createFormControl(props = {}) {
|
|
1647
1982
|
e.persist && e.persist();
|
1648
1983
|
}
|
1649
1984
|
let hasNoPromiseError = true;
|
1650
|
-
let fieldValues =
|
1651
|
-
? cloneObject(_formValues)
|
1652
|
-
: Object.assign({}, _formValues);
|
1985
|
+
let fieldValues = cloneObject(_formValues);
|
1653
1986
|
_subjects.state.next({
|
1654
1987
|
isSubmitting: true,
|
1655
1988
|
});
|
@@ -1671,7 +2004,9 @@ function createFormControl(props = {}) {
|
|
1671
2004
|
await onValid(fieldValues, e);
|
1672
2005
|
}
|
1673
2006
|
else {
|
1674
|
-
|
2007
|
+
if (onInvalid) {
|
2008
|
+
await onInvalid({ ..._formState.errors }, e);
|
2009
|
+
}
|
1675
2010
|
_options.shouldFocusError &&
|
1676
2011
|
focusFieldBy(_fields, (key) => get(_formState.errors, key), _names.mount);
|
1677
2012
|
}
|
@@ -1713,7 +2048,7 @@ function createFormControl(props = {}) {
|
|
1713
2048
|
unset(_formState.errors, name);
|
1714
2049
|
_proxyFormState.isValid && _updateValid();
|
1715
2050
|
}
|
1716
|
-
_subjects.state.next(
|
2051
|
+
_subjects.state.next({ ..._formState });
|
1717
2052
|
}
|
1718
2053
|
};
|
1719
2054
|
const reset = (formValues, keepStateOptions = {}) => {
|
@@ -1726,22 +2061,6 @@ function createFormControl(props = {}) {
|
|
1726
2061
|
_defaultValues = updatedValues;
|
1727
2062
|
}
|
1728
2063
|
if (!keepStateOptions.keepValues) {
|
1729
|
-
if (isWeb && isUndefined(formValues)) {
|
1730
|
-
for (const name of _names.mount) {
|
1731
|
-
const field = get(_fields, name);
|
1732
|
-
if (field && field._f) {
|
1733
|
-
const fieldReference = Array.isArray(field._f.refs)
|
1734
|
-
? field._f.refs[0]
|
1735
|
-
: field._f.ref;
|
1736
|
-
try {
|
1737
|
-
isHTMLElement(fieldReference) &&
|
1738
|
-
fieldReference.closest('form').reset();
|
1739
|
-
break;
|
1740
|
-
}
|
1741
|
-
catch (_a) { }
|
1742
|
-
}
|
1743
|
-
}
|
1744
|
-
}
|
1745
2064
|
_formValues = props.shouldUnregister
|
1746
2065
|
? keepStateOptions.keepDefaultValues
|
1747
2066
|
? cloneObject(_defaultValues)
|
@@ -1780,7 +2099,10 @@ function createFormControl(props = {}) {
|
|
1780
2099
|
dirtyFields: keepStateOptions.keepDirty
|
1781
2100
|
? _formState.dirtyFields
|
1782
2101
|
: (keepStateOptions.keepDefaultValues && formValues
|
1783
|
-
? Object.entries(formValues).reduce((previous, [key, value]) => (
|
2102
|
+
? Object.entries(formValues).reduce((previous, [key, value]) => ({
|
2103
|
+
...previous,
|
2104
|
+
[key]: value !== get(_defaultValues, key),
|
2105
|
+
}), {})
|
1784
2106
|
: {}),
|
1785
2107
|
touchedFields: keepStateOptions.keepTouched
|
1786
2108
|
? _formState.touchedFields
|
@@ -1792,9 +2114,10 @@ function createFormControl(props = {}) {
|
|
1792
2114
|
isSubmitSuccessful: false,
|
1793
2115
|
});
|
1794
2116
|
};
|
1795
|
-
const setFocus = (name) => {
|
2117
|
+
const setFocus = (name, options = {}) => {
|
1796
2118
|
const field = get(_fields, name)._f;
|
1797
|
-
|
2119
|
+
const fieldRef = field.refs ? field.refs[0] : field.ref;
|
2120
|
+
options.shouldSelect ? fieldRef.select() : fieldRef.focus();
|
1798
2121
|
};
|
1799
2122
|
return {
|
1800
2123
|
control: {
|
@@ -1841,7 +2164,10 @@ function createFormControl(props = {}) {
|
|
1841
2164
|
return _options;
|
1842
2165
|
},
|
1843
2166
|
set _options(value) {
|
1844
|
-
_options =
|
2167
|
+
_options = {
|
2168
|
+
..._options,
|
2169
|
+
...value,
|
2170
|
+
};
|
1845
2171
|
},
|
1846
2172
|
},
|
1847
2173
|
trigger,
|
@@ -1860,6 +2186,35 @@ function createFormControl(props = {}) {
|
|
1860
2186
|
};
|
1861
2187
|
}
|
1862
2188
|
|
2189
|
+
/**
|
2190
|
+
* Custom hook to mange the entire form.
|
2191
|
+
*
|
2192
|
+
* @remarks
|
2193
|
+
* [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)
|
2194
|
+
*
|
2195
|
+
* @param props - form configuration and validation parameters.
|
2196
|
+
*
|
2197
|
+
* @returns methods - individual functions to manage the form state. {@link UseFormReturn}
|
2198
|
+
*
|
2199
|
+
* @example
|
2200
|
+
* ```tsx
|
2201
|
+
* function App() {
|
2202
|
+
* const { register, handleSubmit, watch, formState: { errors } } = useForm();
|
2203
|
+
* const onSubmit = data => console.log(data);
|
2204
|
+
*
|
2205
|
+
* console.log(watch("example"));
|
2206
|
+
*
|
2207
|
+
* return (
|
2208
|
+
* <form onSubmit={handleSubmit(onSubmit)}>
|
2209
|
+
* <input defaultValue="test" {...register("example")} />
|
2210
|
+
* <input {...register("exampleRequired", { required: true })} />
|
2211
|
+
* {errors.exampleRequired && <span>This field is required</span>}
|
2212
|
+
* <input type="submit" />
|
2213
|
+
* </form>
|
2214
|
+
* );
|
2215
|
+
* }
|
2216
|
+
* ```
|
2217
|
+
*/
|
1863
2218
|
function useForm(props = {}) {
|
1864
2219
|
const _formControl = React.useRef();
|
1865
2220
|
const [formState, updateFormState] = React.useState({
|
@@ -1878,13 +2233,19 @@ function useForm(props = {}) {
|
|
1878
2233
|
_formControl.current.control._options = props;
|
1879
2234
|
}
|
1880
2235
|
else {
|
1881
|
-
_formControl.current =
|
2236
|
+
_formControl.current = {
|
2237
|
+
...createFormControl(props),
|
2238
|
+
formState,
|
2239
|
+
};
|
1882
2240
|
}
|
1883
2241
|
const control = _formControl.current.control;
|
1884
2242
|
const callback = React.useCallback((value) => {
|
1885
2243
|
if (shouldRenderFormState(value, control._proxyFormState, true)) {
|
1886
|
-
control._formState =
|
1887
|
-
|
2244
|
+
control._formState = {
|
2245
|
+
...control._formState,
|
2246
|
+
...value,
|
2247
|
+
};
|
2248
|
+
updateFormState({ ...control._formState });
|
1888
2249
|
}
|
1889
2250
|
}, [control]);
|
1890
2251
|
useSubscribe({
|