@octanejs/hook-form 0.1.0

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.
Files changed (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/package.json +46 -0
  4. package/src/FormProvider.tsrx +76 -0
  5. package/src/FormProvider.tsrx.d.ts +10 -0
  6. package/src/constants.ts +31 -0
  7. package/src/controller.tsrx +8 -0
  8. package/src/controller.tsrx.d.ts +52 -0
  9. package/src/form.tsrx +119 -0
  10. package/src/form.tsrx.d.ts +6 -0
  11. package/src/formStateSubscribe.tsrx +10 -0
  12. package/src/formStateSubscribe.tsrx.d.ts +16 -0
  13. package/src/index.ts +19 -0
  14. package/src/logic/appendErrors.ts +19 -0
  15. package/src/logic/createFormControl.ts +1841 -0
  16. package/src/logic/generateId.ts +14 -0
  17. package/src/logic/generateWatchOutput.ts +27 -0
  18. package/src/logic/getCheckboxValue.ts +36 -0
  19. package/src/logic/getDirtyFields.ts +120 -0
  20. package/src/logic/getEventValue.ts +12 -0
  21. package/src/logic/getFieldValue.ts +33 -0
  22. package/src/logic/getFieldValueAs.ts +22 -0
  23. package/src/logic/getFocusFieldName.ts +13 -0
  24. package/src/logic/getNodeParentName.ts +4 -0
  25. package/src/logic/getProxyFormState.ts +33 -0
  26. package/src/logic/getRadioValue.ts +24 -0
  27. package/src/logic/getResolverOptions.ts +33 -0
  28. package/src/logic/getRuleValue.ts +16 -0
  29. package/src/logic/getValidateError.ts +22 -0
  30. package/src/logic/getValidationModes.ts +11 -0
  31. package/src/logic/getValueAndMessage.ts +12 -0
  32. package/src/logic/hasPromiseValidation.ts +27 -0
  33. package/src/logic/hasValidation.ts +12 -0
  34. package/src/logic/index.ts +3 -0
  35. package/src/logic/isNameInFieldArray.ts +7 -0
  36. package/src/logic/isWatched.ts +11 -0
  37. package/src/logic/iterateFieldsByAction.ts +37 -0
  38. package/src/logic/schemaErrorLookup.ts +54 -0
  39. package/src/logic/shouldRenderFormState.ts +25 -0
  40. package/src/logic/shouldSubscribeByName.ts +18 -0
  41. package/src/logic/skipValidation.ts +21 -0
  42. package/src/logic/unsetEmptyArray.ts +6 -0
  43. package/src/logic/updateFieldArrayRootError.ts +17 -0
  44. package/src/logic/validateField.ts +273 -0
  45. package/src/types/controller.ts +98 -0
  46. package/src/types/errors.ts +56 -0
  47. package/src/types/events.ts +26 -0
  48. package/src/types/fieldArray.ts +288 -0
  49. package/src/types/fields.ts +46 -0
  50. package/src/types/form.ts +961 -0
  51. package/src/types/index.ts +12 -0
  52. package/src/types/path/common.ts +391 -0
  53. package/src/types/path/eager.ts +224 -0
  54. package/src/types/path/index.ts +16 -0
  55. package/src/types/resolvers.ts +38 -0
  56. package/src/types/utils.ts +125 -0
  57. package/src/types/validator.ts +94 -0
  58. package/src/types/watch.ts +66 -0
  59. package/src/useController.ts +268 -0
  60. package/src/useFieldArray.ts +490 -0
  61. package/src/useForm.ts +182 -0
  62. package/src/useFormContext.ts +47 -0
  63. package/src/useFormControlContext.ts +25 -0
  64. package/src/useFormState.ts +86 -0
  65. package/src/useIsomorphicLayoutEffect.ts +6 -0
  66. package/src/useWatch.ts +356 -0
  67. package/src/utils/append.ts +4 -0
  68. package/src/utils/cloneObject.ts +32 -0
  69. package/src/utils/compact.ts +2 -0
  70. package/src/utils/convertToArrayPayload.ts +2 -0
  71. package/src/utils/createSubject.ts +48 -0
  72. package/src/utils/deepEqual.ts +81 -0
  73. package/src/utils/deepMerge.ts +27 -0
  74. package/src/utils/extractFormValues.ts +28 -0
  75. package/src/utils/fillEmptyArray.ts +3 -0
  76. package/src/utils/flatten.ts +23 -0
  77. package/src/utils/get.ts +29 -0
  78. package/src/utils/index.ts +3 -0
  79. package/src/utils/insert.ts +8 -0
  80. package/src/utils/isBoolean.ts +2 -0
  81. package/src/utils/isCheckBoxInput.ts +4 -0
  82. package/src/utils/isDateObject.ts +2 -0
  83. package/src/utils/isEmptyObject.ts +7 -0
  84. package/src/utils/isFileInput.ts +4 -0
  85. package/src/utils/isFunction.ts +2 -0
  86. package/src/utils/isHTMLElement.ts +13 -0
  87. package/src/utils/isKey.ts +4 -0
  88. package/src/utils/isMultipleSelect.ts +5 -0
  89. package/src/utils/isNullOrUndefined.ts +2 -0
  90. package/src/utils/isObject.ts +8 -0
  91. package/src/utils/isPlainObject.ts +8 -0
  92. package/src/utils/isPrimitive.ts +8 -0
  93. package/src/utils/isRadioInput.ts +4 -0
  94. package/src/utils/isRadioOrCheckbox.ts +8 -0
  95. package/src/utils/isRegex.ts +2 -0
  96. package/src/utils/isString.ts +2 -0
  97. package/src/utils/isUndefined.ts +2 -0
  98. package/src/utils/isWeb.ts +4 -0
  99. package/src/utils/live.ts +6 -0
  100. package/src/utils/move.ts +15 -0
  101. package/src/utils/noop.ts +2 -0
  102. package/src/utils/objectHasFunction.ts +11 -0
  103. package/src/utils/prepend.ts +7 -0
  104. package/src/utils/remove.ts +24 -0
  105. package/src/utils/set.ts +36 -0
  106. package/src/utils/sleep.ts +2 -0
  107. package/src/utils/stringToPath.ts +4 -0
  108. package/src/utils/swap.ts +4 -0
  109. package/src/utils/unset.ts +65 -0
  110. package/src/utils/update.ts +5 -0
  111. package/src/watch.tsrx +7 -0
  112. package/src/watch.tsrx.d.ts +42 -0
@@ -0,0 +1,490 @@
1
+ // Vendored from react-hook-form@7.81.0 src/useFieldArray.ts (octane port).
2
+ import { useState, useRef, useEffect, useMemo, useCallback } from 'octane';
3
+
4
+ import generateId from './logic/generateId';
5
+ import getFocusFieldName from './logic/getFocusFieldName';
6
+ import getValidationModes from './logic/getValidationModes';
7
+ import isWatched from './logic/isWatched';
8
+ import iterateFieldsByAction from './logic/iterateFieldsByAction';
9
+ import updateFieldArrayRootError from './logic/updateFieldArrayRootError';
10
+ import validateField from './logic/validateField';
11
+ import appendAt from './utils/append';
12
+ import cloneObject from './utils/cloneObject';
13
+ import convertToArrayPayload from './utils/convertToArrayPayload';
14
+ import fillEmptyArray from './utils/fillEmptyArray';
15
+ import get from './utils/get';
16
+ import insertAt from './utils/insert';
17
+ import isBoolean from './utils/isBoolean';
18
+ import isEmptyObject from './utils/isEmptyObject';
19
+ import isObject from './utils/isObject';
20
+ import moveArrayAt from './utils/move';
21
+ import prependAt from './utils/prepend';
22
+ import removeArrayAt from './utils/remove';
23
+ import set from './utils/set';
24
+ import swapArrayAt from './utils/swap';
25
+ import unset from './utils/unset';
26
+ import updateAt from './utils/update';
27
+ import { VALIDATION_MODE } from './constants';
28
+ import type {
29
+ Control,
30
+ Field,
31
+ FieldArray,
32
+ FieldArrayMethodProps,
33
+ FieldArrayPath,
34
+ FieldArrayWithId,
35
+ FieldError,
36
+ FieldErrors,
37
+ FieldPath,
38
+ FieldValues,
39
+ FormState,
40
+ InternalFieldName,
41
+ RegisterOptions,
42
+ UseFieldArrayProps,
43
+ UseFieldArrayReturn,
44
+ } from './types';
45
+ import { useFormControlContext } from './useFormControlContext';
46
+ import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
47
+
48
+ /**
49
+ * A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc. • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn) • [Video](https://youtu.be/4MrbfGSFY2A)
50
+ *
51
+ * @remarks
52
+ * [API](https://react-hook-form.com/docs/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
53
+ *
54
+ * @param props - useFieldArray props
55
+ *
56
+ * @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
57
+ *
58
+ * @example
59
+ * ```tsx
60
+ * function App() {
61
+ * const { register, control, handleSubmit, reset, trigger, setError } = useForm({
62
+ * defaultValues: {
63
+ * test: []
64
+ * }
65
+ * });
66
+ * const { fields, append } = useFieldArray({
67
+ * control,
68
+ * name: "test"
69
+ * });
70
+ *
71
+ * return (
72
+ * <form onSubmit={handleSubmit(data => console.log(data))}>
73
+ * {fields.map((item, index) => (
74
+ * <input key={item.id} {...register(`test.${index}.firstName`)} />
75
+ * ))}
76
+ * <button type="button" onClick={() => append({ firstName: "bill" })}>
77
+ * append
78
+ * </button>
79
+ * <input type="submit" />
80
+ * </form>
81
+ * );
82
+ * }
83
+ * ```
84
+ */
85
+ export function useFieldArray<
86
+ TFieldValues extends FieldValues = FieldValues,
87
+ TFieldArrayName extends FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
88
+ TKeyName extends string = 'id',
89
+ TTransformedValues = TFieldValues,
90
+ >(
91
+ props: UseFieldArrayProps<TFieldValues, TFieldArrayName, TKeyName, TTransformedValues>,
92
+ ): UseFieldArrayReturn<TFieldValues, TFieldArrayName, TKeyName> {
93
+ const formControl = useFormControlContext<TFieldValues, any, TTransformedValues>();
94
+ const { control = formControl, name, keyName = 'id', disabled, shouldUnregister, rules } = props;
95
+ const [fields, setFields] = useState(control._getFieldArray(name));
96
+ const ids = useRef<string[]>(control._getFieldArray(name).map(generateId));
97
+
98
+ const _actioned = useRef(false);
99
+
100
+ if (!disabled) {
101
+ control._names.array.add(name);
102
+ }
103
+
104
+ useMemo(
105
+ () =>
106
+ !disabled &&
107
+ rules &&
108
+ fields.length >= 0 &&
109
+ (control as Control<TFieldValues, any, TTransformedValues>).register(
110
+ name as FieldPath<TFieldValues>,
111
+ rules as RegisterOptions<TFieldValues>,
112
+ ),
113
+ [control, name, fields.length, rules, disabled],
114
+ );
115
+
116
+ useIsomorphicLayoutEffect(() => {
117
+ if (disabled) {
118
+ return;
119
+ }
120
+
121
+ return control._subjects.array.subscribe({
122
+ next: ({
123
+ values,
124
+ name: fieldArrayName,
125
+ }: {
126
+ values?: FieldValues;
127
+ name?: InternalFieldName;
128
+ }) => {
129
+ if (fieldArrayName === name || !fieldArrayName) {
130
+ const fieldValues = get(values, name);
131
+ if (Array.isArray(fieldValues)) {
132
+ setFields(fieldValues);
133
+ ids.current = fieldValues.map(generateId);
134
+ } else if (!fieldArrayName) {
135
+ setFields([]);
136
+ ids.current = [];
137
+ }
138
+ }
139
+ },
140
+ }).unsubscribe;
141
+ }, [control, name, disabled]);
142
+
143
+ const updateValues = useCallback(
144
+ <T extends Partial<FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>>[]>(
145
+ updatedFieldArrayValues: T,
146
+ ) => {
147
+ _actioned.current = true;
148
+ control._setFieldArray(name, updatedFieldArrayValues);
149
+ },
150
+ [control, name],
151
+ );
152
+
153
+ const append = (
154
+ value:
155
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>
156
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>[],
157
+ options?: FieldArrayMethodProps,
158
+ ) => {
159
+ if (disabled) {
160
+ return;
161
+ }
162
+
163
+ const appendValue = convertToArrayPayload(cloneObject(value));
164
+ const updatedFieldArrayValues = appendAt(control._getFieldArray(name), appendValue);
165
+ control._names.focus = getFocusFieldName(name, updatedFieldArrayValues.length - 1, options);
166
+ ids.current = appendAt(ids.current, appendValue.map(generateId));
167
+ updateValues(updatedFieldArrayValues);
168
+ setFields(updatedFieldArrayValues);
169
+ control._setFieldArray(name, updatedFieldArrayValues, appendAt, {
170
+ argA: fillEmptyArray(value),
171
+ });
172
+ };
173
+
174
+ const prepend = (
175
+ value:
176
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>
177
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>[],
178
+ options?: FieldArrayMethodProps,
179
+ ) => {
180
+ if (disabled) {
181
+ return;
182
+ }
183
+
184
+ const prependValue = convertToArrayPayload(cloneObject(value));
185
+ const updatedFieldArrayValues = prependAt(control._getFieldArray(name), prependValue);
186
+ control._names.focus = getFocusFieldName(name, 0, options);
187
+ ids.current = prependAt(ids.current, prependValue.map(generateId));
188
+ updateValues(updatedFieldArrayValues);
189
+ setFields(updatedFieldArrayValues);
190
+ control._setFieldArray(name, updatedFieldArrayValues, prependAt, {
191
+ argA: fillEmptyArray(value),
192
+ });
193
+ };
194
+
195
+ const remove = (index?: number | number[]) => {
196
+ if (disabled) {
197
+ return;
198
+ }
199
+
200
+ const updatedFieldArrayValues: Partial<
201
+ FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>
202
+ >[] = removeArrayAt(control._getFieldArray(name), index);
203
+ ids.current = removeArrayAt(ids.current, index);
204
+ updateValues(updatedFieldArrayValues);
205
+ setFields(updatedFieldArrayValues);
206
+ !Array.isArray(get(control._fields, name)) && set(control._fields, name, undefined);
207
+ control._setFieldArray(name, updatedFieldArrayValues, removeArrayAt, {
208
+ argA: index,
209
+ });
210
+ };
211
+
212
+ const insert = (
213
+ index: number,
214
+ value:
215
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>
216
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>[],
217
+ options?: FieldArrayMethodProps,
218
+ ) => {
219
+ if (disabled) {
220
+ return;
221
+ }
222
+
223
+ const insertValue = convertToArrayPayload(cloneObject(value));
224
+ const updatedFieldArrayValues = insertAt(control._getFieldArray(name), index, insertValue);
225
+ control._names.focus = getFocusFieldName(name, index, options);
226
+ ids.current = insertAt(ids.current, index, insertValue.map(generateId));
227
+ updateValues(updatedFieldArrayValues);
228
+ setFields(updatedFieldArrayValues);
229
+ control._setFieldArray(name, updatedFieldArrayValues, insertAt, {
230
+ argA: index,
231
+ argB: fillEmptyArray(value),
232
+ });
233
+ };
234
+
235
+ const swap = (indexA: number, indexB: number) => {
236
+ if (disabled) {
237
+ return;
238
+ }
239
+
240
+ const updatedFieldArrayValues = control._getFieldArray(name);
241
+ swapArrayAt(updatedFieldArrayValues, indexA, indexB);
242
+ swapArrayAt(ids.current, indexA, indexB);
243
+ updateValues(updatedFieldArrayValues);
244
+ setFields(updatedFieldArrayValues);
245
+ control._setFieldArray(
246
+ name,
247
+ updatedFieldArrayValues,
248
+ swapArrayAt,
249
+ {
250
+ argA: indexA,
251
+ argB: indexB,
252
+ },
253
+ false,
254
+ );
255
+ };
256
+
257
+ const move = (from: number, to: number) => {
258
+ if (disabled) {
259
+ return;
260
+ }
261
+
262
+ const updatedFieldArrayValues = control._getFieldArray(name);
263
+ moveArrayAt(updatedFieldArrayValues, from, to);
264
+ moveArrayAt(ids.current, from, to);
265
+ updateValues(updatedFieldArrayValues);
266
+ setFields(updatedFieldArrayValues);
267
+ control._setFieldArray(
268
+ name,
269
+ updatedFieldArrayValues,
270
+ moveArrayAt,
271
+ {
272
+ argA: from,
273
+ argB: to,
274
+ },
275
+ false,
276
+ );
277
+ };
278
+
279
+ const update = (index: number, value: FieldArray<TFieldValues, TFieldArrayName>) => {
280
+ if (disabled) {
281
+ return;
282
+ }
283
+
284
+ const updateValue = cloneObject(value);
285
+ const updatedFieldArrayValues = updateAt(
286
+ control._getFieldArray<FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>>(name),
287
+ index,
288
+ updateValue as FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>,
289
+ );
290
+ ids.current = [...updatedFieldArrayValues].map((item, i) =>
291
+ !item || i === index ? generateId() : ids.current[i],
292
+ );
293
+ updateValues(updatedFieldArrayValues);
294
+ setFields([...updatedFieldArrayValues]);
295
+ control._setFieldArray(
296
+ name,
297
+ updatedFieldArrayValues,
298
+ updateAt,
299
+ {
300
+ argA: index,
301
+ argB: updateValue,
302
+ },
303
+ true,
304
+ false,
305
+ );
306
+ };
307
+
308
+ const replace = (
309
+ value:
310
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>
311
+ | Partial<FieldArray<TFieldValues, TFieldArrayName>>[],
312
+ ) => {
313
+ if (disabled) {
314
+ return;
315
+ }
316
+
317
+ const updatedFieldArrayValues = convertToArrayPayload(cloneObject(value));
318
+ ids.current = updatedFieldArrayValues.map(generateId);
319
+ updateValues([...updatedFieldArrayValues]);
320
+ setFields([...updatedFieldArrayValues]);
321
+ control._setFieldArray(
322
+ name,
323
+ [...updatedFieldArrayValues],
324
+ <T>(data: T): T => data,
325
+ {},
326
+ true,
327
+ false,
328
+ );
329
+ };
330
+
331
+ useEffect(() => {
332
+ if (disabled) {
333
+ return;
334
+ }
335
+
336
+ control._state.action = false;
337
+
338
+ isWatched(name, control._names) &&
339
+ control._subjects.state.next({
340
+ ...control._formState,
341
+ } as FormState<TFieldValues>);
342
+
343
+ const validationModes = getValidationModes(control._options.mode);
344
+
345
+ if (
346
+ _actioned.current &&
347
+ (!validationModes.isOnSubmit || control._formState.isSubmitted) &&
348
+ !getValidationModes(control._options.reValidateMode).isOnSubmit &&
349
+ !validationModes.isOnBlur
350
+ ) {
351
+ if (control._options.resolver) {
352
+ control._runSchema([name]).then((result) => {
353
+ control._updateIsValidating([name]);
354
+ const error = get(result.errors, name);
355
+ const existingError = get(control._formState.errors, name);
356
+ const existingErrorType =
357
+ existingError && (existingError.type || existingError.root?.type);
358
+ const existingErrorMessage =
359
+ existingError && (existingError.message || existingError.root?.message);
360
+
361
+ if (
362
+ existingError
363
+ ? (!error && existingErrorType) ||
364
+ (error &&
365
+ (existingErrorType !== error.type || existingErrorMessage !== error.message))
366
+ : error && error.type
367
+ ) {
368
+ if (error) {
369
+ isObject(error) && !Object.keys(error).some((key) => !Number.isNaN(+key))
370
+ ? updateFieldArrayRootError(
371
+ control._formState.errors as FieldErrors<TFieldValues>,
372
+ { [name]: error } as Partial<Record<string, FieldError>>,
373
+ name,
374
+ )
375
+ : set(control._formState.errors, name, error);
376
+ } else {
377
+ unset(control._formState.errors, name);
378
+ }
379
+ control._subjects.state.next({
380
+ errors: control._formState.errors as FieldErrors<TFieldValues>,
381
+ });
382
+ }
383
+ });
384
+ } else {
385
+ const field: Field = get(control._fields, name);
386
+ if (
387
+ field &&
388
+ field._f &&
389
+ !(
390
+ getValidationModes(control._options.reValidateMode).isOnSubmit &&
391
+ getValidationModes(control._options.mode).isOnSubmit
392
+ )
393
+ ) {
394
+ validateField(
395
+ field,
396
+ control._names.disabled,
397
+ control._formValues,
398
+ control._options.criteriaMode === VALIDATION_MODE.all,
399
+ control._options.shouldUseNativeValidation,
400
+ true,
401
+ ).then(
402
+ (error) =>
403
+ !isEmptyObject(error) &&
404
+ control._subjects.state.next({
405
+ errors: updateFieldArrayRootError(
406
+ control._formState.errors as FieldErrors<TFieldValues>,
407
+ error,
408
+ name,
409
+ ) as FieldErrors<TFieldValues>,
410
+ }),
411
+ );
412
+ }
413
+ }
414
+ }
415
+
416
+ // External updates that change `fields` (e.g. reset() or setValue() on
417
+ // the array) already notify subscribers with the up-to-date values
418
+ // themselves, so only re-broadcast here for genuine array method calls.
419
+ _actioned.current &&
420
+ control._subjects.state.next({
421
+ name,
422
+ values: cloneObject(control._formValues) as TFieldValues,
423
+ });
424
+
425
+ control._names.focus &&
426
+ iterateFieldsByAction(control._fields, (ref, key: string) => {
427
+ if (control._names.focus && key.startsWith(control._names.focus) && ref.focus) {
428
+ ref.focus();
429
+ return 1;
430
+ }
431
+ return;
432
+ });
433
+
434
+ control._names.focus = '';
435
+
436
+ control._setValid();
437
+ _actioned.current = false;
438
+ }, [fields, name, control, disabled]);
439
+
440
+ useEffect(() => {
441
+ if (!disabled) {
442
+ !get(control._formValues, name) && control._setFieldArray(name);
443
+ }
444
+
445
+ return () => {
446
+ if (disabled) {
447
+ return;
448
+ }
449
+
450
+ const shouldKeepFieldArrayValues = !(control._options.shouldUnregister || shouldUnregister);
451
+ const updateMounted = (name: InternalFieldName, value: boolean) => {
452
+ const field: Field = get(control._fields, name);
453
+ if (field && field._f) {
454
+ field._f.mount = value;
455
+ }
456
+ };
457
+
458
+ if (_actioned.current && shouldKeepFieldArrayValues) {
459
+ control._subjects.state.next({
460
+ name,
461
+ values: cloneObject(control._formValues) as TFieldValues,
462
+ });
463
+ }
464
+
465
+ shouldKeepFieldArrayValues
466
+ ? updateMounted(name, false)
467
+ : control.unregister(name as FieldPath<TFieldValues>);
468
+ };
469
+ }, [name, control, keyName, shouldUnregister, disabled]);
470
+
471
+ return {
472
+ swap: useCallback(swap, [updateValues, name, control, disabled]),
473
+ move: useCallback(move, [updateValues, name, control, disabled]),
474
+ prepend: useCallback(prepend, [updateValues, name, control, disabled]),
475
+ append: useCallback(append, [updateValues, name, control, disabled]),
476
+ remove: useCallback(remove, [updateValues, name, control, disabled]),
477
+ insert: useCallback(insert, [updateValues, name, control, disabled]),
478
+ update: useCallback(update, [updateValues, name, control, disabled]),
479
+ replace: useCallback(replace, [updateValues, name, control, disabled]),
480
+ fields: useMemo(
481
+ () =>
482
+ fields.map((field, index) => ({
483
+ ...field,
484
+ ...(isBoolean(disabled) ? { disabled } : {}),
485
+ [keyName]: ids.current[index] || generateId(),
486
+ })) as FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>[],
487
+ [fields, keyName, disabled],
488
+ ),
489
+ };
490
+ }
package/src/useForm.ts ADDED
@@ -0,0 +1,182 @@
1
+ // Vendored from react-hook-form@7.81.0 src/useForm.ts (octane port).
2
+ import { useState, useRef, useEffect, useMemo } from 'octane';
3
+
4
+ import { DEFAULT_FORM_STATE } from './logic/createFormControl';
5
+ import getProxyFormState from './logic/getProxyFormState';
6
+ import cloneObject from './utils/cloneObject';
7
+ import deepEqual from './utils/deepEqual';
8
+ import isFunction from './utils/isFunction';
9
+ import { createFormControl } from './logic';
10
+ import type { FieldValues, FormState, UseFormProps, UseFormReturn } from './types';
11
+ import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
12
+
13
+ /**
14
+ * Custom hook to manage the entire form.
15
+ *
16
+ * @remarks
17
+ * [API](https://react-hook-form.com/docs/useform) • [Demo](https://codesandbox.io/s/react-hook-form-get-started-ts-5ksmm) • [Video](https://www.youtube.com/watch?v=RkXv4AXXC_4)
18
+ *
19
+ * @param props - form configuration and validation parameters.
20
+ *
21
+ * @returns methods - individual functions to manage the form state. {@link UseFormReturn}
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * function App() {
26
+ * const { register, handleSubmit, watch, formState: { errors } } = useForm();
27
+ * const onSubmit = data => console.log(data);
28
+ *
29
+ * console.log(watch("example"));
30
+ *
31
+ * return (
32
+ * <form onSubmit={handleSubmit(onSubmit)}>
33
+ * <input defaultValue="test" {...register("example")} />
34
+ * <input {...register("exampleRequired", { required: true })} />
35
+ * {errors.exampleRequired && <span>This field is required</span>}
36
+ * <button>Submit</button>
37
+ * </form>
38
+ * );
39
+ * }
40
+ * ```
41
+ */
42
+ export function useForm<
43
+ TFieldValues extends FieldValues = FieldValues,
44
+ TContext = any,
45
+ TTransformedValues = TFieldValues,
46
+ >(
47
+ props: UseFormProps<TFieldValues, TContext, TTransformedValues> = {},
48
+ ): UseFormReturn<TFieldValues, TContext, TTransformedValues> {
49
+ const _formControl = useRef<
50
+ UseFormReturn<TFieldValues, TContext, TTransformedValues> | undefined
51
+ >(undefined);
52
+ const _values = useRef<typeof props.values>(undefined);
53
+ const _formControlProp = useRef(props.formControl);
54
+ const [formState, updateFormState] = useState<FormState<TFieldValues>>(() => ({
55
+ ...cloneObject(DEFAULT_FORM_STATE),
56
+ isLoading: isFunction(props.defaultValues),
57
+ errors: props.errors || {},
58
+ disabled: props.disabled || false,
59
+ defaultValues: isFunction(props.defaultValues) ? undefined : props.defaultValues,
60
+ }));
61
+
62
+ if (
63
+ !_formControl.current ||
64
+ (props.formControl && _formControlProp.current !== props.formControl)
65
+ ) {
66
+ _formControlProp.current = props.formControl;
67
+ if (props.formControl) {
68
+ _formControl.current = {
69
+ ...props.formControl,
70
+ formState,
71
+ };
72
+
73
+ if (props.defaultValues && !isFunction(props.defaultValues)) {
74
+ props.formControl.reset(props.defaultValues, props.resetOptions);
75
+ }
76
+ } else {
77
+ const { formControl, ...rest } = createFormControl(props);
78
+
79
+ _formControl.current = {
80
+ ...rest,
81
+ formState,
82
+ };
83
+ }
84
+ }
85
+
86
+ const control = _formControl.current.control;
87
+ control._options = props;
88
+
89
+ useIsomorphicLayoutEffect(() => {
90
+ const sub = control._subscribe({
91
+ formState: control._proxyFormState,
92
+ callback: () =>
93
+ updateFormState({
94
+ ...control._formState,
95
+ defaultValues: control._defaultValues as FormState<TFieldValues>['defaultValues'],
96
+ }),
97
+ reRenderRoot: true,
98
+ });
99
+
100
+ updateFormState((data) => ({
101
+ ...data,
102
+ isReady: true,
103
+ }));
104
+
105
+ control._formState.isReady = true;
106
+
107
+ return sub;
108
+ }, [control]);
109
+
110
+ useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
111
+
112
+ useEffect(() => {
113
+ if (props.mode) {
114
+ control._options.mode = props.mode;
115
+ }
116
+ if (props.reValidateMode) {
117
+ control._options.reValidateMode = props.reValidateMode;
118
+ }
119
+ }, [control, props.mode, props.reValidateMode]);
120
+
121
+ useEffect(() => {
122
+ if (props.errors) {
123
+ control._setErrors(props.errors);
124
+ control._focusError();
125
+ }
126
+ }, [control, props.errors]);
127
+
128
+ useEffect(() => {
129
+ props.shouldUnregister &&
130
+ control._subjects.state.next({
131
+ values: control._getWatch(),
132
+ });
133
+ }, [control, props.shouldUnregister]);
134
+
135
+ useEffect(() => {
136
+ if (control._proxyFormState.isDirty) {
137
+ const isDirty = control._getDirty();
138
+ if (isDirty !== formState.isDirty) {
139
+ control._subjects.state.next({
140
+ isDirty,
141
+ });
142
+ }
143
+ }
144
+ }, [control, formState.isDirty]);
145
+
146
+ useEffect(() => {
147
+ if (props.values && !deepEqual(props.values, _values.current)) {
148
+ control._reset(props.values, {
149
+ keepFieldsRef: true,
150
+ ...control._options.resetOptions,
151
+ });
152
+ if (!control._options.resetOptions?.keepIsValid) {
153
+ control._setValid();
154
+ }
155
+ _values.current = props.values;
156
+ updateFormState((state) => ({ ...state }));
157
+ } else {
158
+ control._resetDefaultValues();
159
+ }
160
+ }, [control, props.values]);
161
+
162
+ useEffect(() => {
163
+ if (!control._state.mount) {
164
+ control._setValid();
165
+ control._state.mount = true;
166
+ }
167
+
168
+ if (control._state.watch) {
169
+ control._state.watch = false;
170
+ control._subjects.state.next({ ...control._formState });
171
+ }
172
+
173
+ control._removeUnmounted();
174
+ });
175
+
176
+ _formControl.current.formState = useMemo(
177
+ () => getProxyFormState(formState, control),
178
+ [control, formState],
179
+ );
180
+
181
+ return _formControl.current;
182
+ }