remix-validated-form 4.6.12 → 5.0.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.
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ type GetInputProps = <T extends MinimalInputProps>(props?: Omit<T, HandledProps
22
22
 
23
23
  /**
24
24
  * Returns whether or not the parent form is currently being submitted.
25
- * This is different from Remix's `useTransition().submission` in that it
25
+ * This is different from Remix's `useNavigation()` in that it
26
26
  * is aware of what form it's in and when _that_ form is being submitted.
27
27
  *
28
28
  * @param formId
@@ -87,6 +87,8 @@ declare const useField: (name: string, options?: {
87
87
  declare const useControlField: <T>(name: string, formId?: string) => readonly [T, (value: T) => void];
88
88
  declare const useUpdateControlledField: (formId?: string) => (field: string, value: unknown) => void;
89
89
 
90
+ declare const FORM_DEFAULTS_FIELD: "__rvfInternalFormDefaults";
91
+
90
92
  type FieldErrors = Record<string, string>;
91
93
  type TouchedFields = Record<string, boolean>;
92
94
  type GenericObject = {
@@ -130,7 +132,10 @@ type ValidateFieldResult = {
130
132
  */
131
133
  type Validator<DataType> = {
132
134
  validate: (unvalidatedData: GenericObject) => Promise<ValidationResult<DataType>>;
133
- validateField: (unvalidatedData: GenericObject, field: string) => Promise<ValidateFieldResult>;
135
+ /**
136
+ * @deprecated Will be removed in a future version of remix-validated-form
137
+ */
138
+ validateField?: (unvalidatedData: GenericObject, field: string) => Promise<ValidateFieldResult>;
134
139
  };
135
140
  type Valid<DataType> = {
136
141
  data: DataType;
@@ -164,9 +169,14 @@ declare function validationError(error: ValidatorError, repopulateFields?: unkno
164
169
  type FormDefaults = {
165
170
  [formDefaultsKey: `${typeof FORM_DEFAULTS_FIELD}_${string}`]: any;
166
171
  };
172
+ type internal_FORM_DEFAULTS_FIELD = typeof FORM_DEFAULTS_FIELD;
167
173
  declare const setFormDefaults: <DataType = any>(formId: string, defaultValues: Partial<DataType>) => FormDefaults;
168
174
 
169
- type FormProps<DataType> = {
175
+ type SubactionData<DataType, Subaction extends string | undefined> = DataType & {
176
+ subaction: Subaction;
177
+ };
178
+ type DefaultValuesForSubaction<DataType, Subaction extends string | undefined> = Subaction extends string ? SubactionData<DataType, Subaction> extends undefined ? DataType : SubactionData<DataType, Subaction> : DataType;
179
+ type FormProps<DataType, Subaction extends string | undefined> = {
170
180
  /**
171
181
  * A `Validator` object that describes how to validate the form.
172
182
  */
@@ -186,7 +196,7 @@ type FormProps<DataType> = {
186
196
  * Accepts an object of default values for the form
187
197
  * that will automatically be propagated to the form fields via `useField`.
188
198
  */
189
- defaultValues?: Partial<DataType>;
199
+ defaultValues?: Partial<DefaultValuesForSubaction<DataType, Subaction>>;
190
200
  /**
191
201
  * A ref to the form element.
192
202
  */
@@ -196,7 +206,7 @@ type FormProps<DataType> = {
196
206
  * Setting a value here will cause the form to be submitted with an extra `subaction` value.
197
207
  * This can be useful when there are multiple forms on the screen handled by the same action.
198
208
  */
199
- subaction?: string;
209
+ subaction?: Subaction;
200
210
  /**
201
211
  * Reset the form to the default values after the form has been successfully submitted.
202
212
  * This is useful if you want to submit the same form multiple times,
@@ -212,7 +222,7 @@ type FormProps<DataType> = {
212
222
  /**
213
223
  * The primary form component of `remix-validated-form`.
214
224
  */
215
- declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues: unMemoizedDefaults, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, id, ...rest }: FormProps<DataType>): JSX.Element;
225
+ declare function ValidatedForm<DataType, Subaction extends string | undefined>({ validator, onSubmit, children, fetcher, action, defaultValues: unMemoizedDefaults, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, id, preventScrollReset, relative, encType, ...rest }: FormProps<DataType, Subaction>): JSX.Element;
216
226
 
217
227
  /**
218
228
  * Used to create a validator for a form.
@@ -311,6 +321,18 @@ type FieldArrayValidationBehaviorOptions = {
311
321
  initial: FieldArrayValidationBehavior;
312
322
  whenSubmitted: FieldArrayValidationBehavior;
313
323
  };
324
+ type FieldArrayItem<T> = {
325
+ /**
326
+ * The default value of the item.
327
+ * This does not update as the field is changed by the user.
328
+ */
329
+ defaultValue: T;
330
+ /**
331
+ * A unique key for the item.
332
+ * Use this as the key prop when rendering the item.
333
+ */
334
+ key: string;
335
+ };
314
336
  type FieldArrayHelpers<Item = any> = {
315
337
  push: (item: Item) => void;
316
338
  swap: (indexA: number, indexB: number) => void;
@@ -325,13 +347,13 @@ type UseFieldArrayOptions = {
325
347
  formId?: string;
326
348
  validationBehavior?: Partial<FieldArrayValidationBehaviorOptions>;
327
349
  };
328
- declare function useFieldArray<Item = any>(name: string, { formId, validationBehavior }?: UseFieldArrayOptions): [itemDefaults: Item[], helpers: FieldArrayHelpers<any>, error: string | undefined];
329
- type FieldArrayProps = {
350
+ declare function useFieldArray<Item = any>(name: string, { formId, validationBehavior }?: UseFieldArrayOptions): [items: FieldArrayItem<Item>[], helpers: FieldArrayHelpers<any>, error: string | undefined];
351
+ type FieldArrayProps<Item> = {
330
352
  name: string;
331
- children: (itemDefaults: any[], helpers: FieldArrayHelpers, error: string | undefined) => React.ReactNode;
353
+ children: (items: FieldArrayItem<Item>[], helpers: FieldArrayHelpers<Item>, error: string | undefined) => React.ReactNode;
332
354
  formId?: string;
333
355
  validationBehavior?: FieldArrayValidationBehaviorOptions;
334
356
  };
335
- declare const FieldArray: ({ name, children, formId, validationBehavior, }: FieldArrayProps) => JSX.Element;
357
+ declare function FieldArray<Item = any>({ name, children, formId, validationBehavior, }: FieldArrayProps<Item>): JSX.Element;
336
358
 
337
- export { BaseResult, CreateValidatorArg, ErrorResult, FieldArray, FieldArrayHelpers, FieldArrayProps, FieldErrors, FieldProps, FormContextValue, FormDefaults, FormProps, GenericObject, Invalid, SuccessResult, TouchedFields, Valid, ValidateFieldResult, ValidatedForm, ValidationErrorResponseData, ValidationResult, Validator, ValidatorData, ValidatorError, createValidator, setFormDefaults, useControlField, useField, useFieldArray, useFormContext, useIsSubmitting, useIsValid, useUpdateControlledField, validationError };
359
+ export { BaseResult, CreateValidatorArg, ErrorResult, FieldArray, FieldArrayHelpers, FieldArrayProps, FieldErrors, FieldProps, FormContextValue, FormDefaults, FormProps, GenericObject, Invalid, SuccessResult, TouchedFields, Valid, ValidateFieldResult, ValidatedForm, ValidationErrorResponseData, ValidationResult, Validator, ValidatorData, ValidatorError, createValidator, internal_FORM_DEFAULTS_FIELD, setFormDefaults, useControlField, useField, useFieldArray, useFormContext, useIsSubmitting, useIsValid, useUpdateControlledField, validationError };
package/dist/index.esm.js CHANGED
@@ -85,7 +85,7 @@ var createGetInputProps = ({
85
85
  };
86
86
 
87
87
  // src/internal/hooks.ts
88
- import { useActionData, useMatches, useTransition } from "@remix-run/react";
88
+ import { useActionData, useMatches, useNavigation } from "@remix-run/react";
89
89
  import { useCallback, useContext } from "react";
90
90
 
91
91
  // ../set-get/src/stringToPathArray.ts
@@ -266,7 +266,9 @@ function sparseSplice(array, start, deleteCount, item) {
266
266
  }
267
267
  if (arguments.length === 4)
268
268
  return array.splice(start, deleteCount, item);
269
- return array.splice(start, deleteCount);
269
+ else if (arguments.length === 3)
270
+ return array.splice(start, deleteCount);
271
+ return array.splice(start);
270
272
  }
271
273
  var move = (array, from2, to) => {
272
274
  const [item] = sparseSplice(array, from2, 1);
@@ -275,6 +277,12 @@ var move = (array, from2, to) => {
275
277
  var insert = (array, index, value) => {
276
278
  sparseSplice(array, index, 0, value);
277
279
  };
280
+ var insertEmpty = (array, index) => {
281
+ const tail = sparseSplice(array, index);
282
+ tail.forEach((item, i) => {
283
+ sparseSplice(array, index + i + 1, 0, item);
284
+ });
285
+ };
278
286
  var remove = (array, index) => {
279
287
  sparseSplice(array, index, 1);
280
288
  };
@@ -404,6 +412,29 @@ if (void 0) {
404
412
  expect(array).toEqual([true, void 0, void 0, true]);
405
413
  });
406
414
  });
415
+ describe("insertEmpty", () => {
416
+ it("should insert an empty item at a given index", () => {
417
+ const array = [1, 2, 3];
418
+ insertEmpty(array, 1);
419
+ expect(array).toStrictEqual([1, , 2, 3]);
420
+ expect(array).not.toStrictEqual([1, void 0, 2, 3]);
421
+ });
422
+ it("should work with already sparse arrays", () => {
423
+ const array = [, , 1, , 2, , 3];
424
+ insertEmpty(array, 3);
425
+ expect(array).toStrictEqual([, , 1, , , 2, , 3]);
426
+ expect(array).not.toStrictEqual([
427
+ void 0,
428
+ void 0,
429
+ 1,
430
+ void 0,
431
+ void 0,
432
+ 2,
433
+ void 0,
434
+ 3
435
+ ]);
436
+ });
437
+ });
407
438
  describe("remove", () => {
408
439
  it("should remove an item at a given index", () => {
409
440
  const array = [1, 2, 3];
@@ -585,10 +616,12 @@ var defaultFormState = {
585
616
  reset: () => noOp,
586
617
  syncFormProps: noOp,
587
618
  setFormElement: noOp,
588
- validateField: async () => null,
589
619
  validate: async () => {
590
620
  throw new Error("Validate called before form was initialized.");
591
621
  },
622
+ smartValidate: async () => {
623
+ throw new Error("Validate called before form was initialized.");
624
+ },
592
625
  submit: async () => {
593
626
  throw new Error("Submit called before form was initialized.");
594
627
  },
@@ -671,8 +704,8 @@ var createFormState = (set, get2) => ({
671
704
  state.formElement = formElement;
672
705
  });
673
706
  },
674
- validateField: async (field) => {
675
- var _a, _b, _c;
707
+ validate: async () => {
708
+ var _a;
676
709
  const formElement = get2().formElement;
677
710
  invariant2(
678
711
  formElement,
@@ -681,22 +714,14 @@ var createFormState = (set, get2) => ({
681
714
  const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
682
715
  invariant2(
683
716
  validator,
684
- "Cannot validator. This is probably a bug in remix-validated-form."
685
- );
686
- await ((_c = (_b = get2().controlledFields).awaitValueUpdate) == null ? void 0 : _c.call(_b, field));
687
- const { error } = await validator.validateField(
688
- new FormData(formElement),
689
- field
717
+ "Cannot find validator. This is probably a bug in remix-validated-form."
690
718
  );
691
- if (error) {
692
- get2().setFieldError(field, error);
693
- return error;
694
- } else {
695
- get2().clearFieldError(field);
696
- return null;
697
- }
719
+ const result = await validator.validate(new FormData(formElement));
720
+ if (result.error)
721
+ get2().setFieldErrors(result.error.fieldErrors);
722
+ return result;
698
723
  },
699
- validate: async () => {
724
+ smartValidate: async ({ alwaysIncludeErrorsFromFields = [] } = {}) => {
700
725
  var _a;
701
726
  const formElement = get2().formElement;
702
727
  invariant2(
@@ -706,12 +731,75 @@ var createFormState = (set, get2) => ({
706
731
  const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
707
732
  invariant2(
708
733
  validator,
709
- "Cannot validator. This is probably a bug in remix-validated-form."
734
+ "Cannot find validator. This is probably a bug in remix-validated-form."
710
735
  );
711
- const result = await validator.validate(new FormData(formElement));
712
- if (result.error)
713
- get2().setFieldErrors(result.error.fieldErrors);
714
- return result;
736
+ await Promise.all(
737
+ alwaysIncludeErrorsFromFields.map(
738
+ (field) => {
739
+ var _a2, _b;
740
+ return (_b = (_a2 = get2().controlledFields).awaitValueUpdate) == null ? void 0 : _b.call(_a2, field);
741
+ }
742
+ )
743
+ );
744
+ const validationResult = await validator.validate(
745
+ new FormData(formElement)
746
+ );
747
+ if (!validationResult.error) {
748
+ const hadErrors = Object.keys(get2().fieldErrors).length > 0;
749
+ if (hadErrors)
750
+ get2().setFieldErrors({});
751
+ return validationResult;
752
+ }
753
+ const {
754
+ error: { fieldErrors }
755
+ } = validationResult;
756
+ const errorFields = /* @__PURE__ */ new Set();
757
+ const incomingErrors = /* @__PURE__ */ new Set();
758
+ const prevErrors = /* @__PURE__ */ new Set();
759
+ Object.keys(fieldErrors).forEach((field) => {
760
+ errorFields.add(field);
761
+ incomingErrors.add(field);
762
+ });
763
+ Object.keys(get2().fieldErrors).forEach((field) => {
764
+ errorFields.add(field);
765
+ prevErrors.add(field);
766
+ });
767
+ const fieldsToUpdate = /* @__PURE__ */ new Set();
768
+ const fieldsToDelete = /* @__PURE__ */ new Set();
769
+ errorFields.forEach((field) => {
770
+ if (!incomingErrors.has(field)) {
771
+ fieldsToDelete.add(field);
772
+ return;
773
+ }
774
+ if (prevErrors.has(field) && incomingErrors.has(field)) {
775
+ if (fieldErrors[field] !== get2().fieldErrors[field])
776
+ fieldsToUpdate.add(field);
777
+ return;
778
+ }
779
+ if (alwaysIncludeErrorsFromFields.includes(field)) {
780
+ fieldsToUpdate.add(field);
781
+ return;
782
+ }
783
+ if (!prevErrors.has(field)) {
784
+ const fieldTouched = get2().touchedFields[field];
785
+ const formHasBeenSubmitted = get2().hasBeenSubmitted;
786
+ if (fieldTouched || formHasBeenSubmitted)
787
+ fieldsToUpdate.add(field);
788
+ return;
789
+ }
790
+ });
791
+ if (fieldsToDelete.size === 0 && fieldsToUpdate.size === 0) {
792
+ return { ...validationResult, error: { fieldErrors: get2().fieldErrors } };
793
+ }
794
+ set((state) => {
795
+ fieldsToDelete.forEach((field) => {
796
+ delete state.fieldErrors[field];
797
+ });
798
+ fieldsToUpdate.forEach((field) => {
799
+ state.fieldErrors[field] = fieldErrors[field];
800
+ });
801
+ });
802
+ return { ...validationResult, error: { fieldErrors: get2().fieldErrors } };
715
803
  },
716
804
  submit: () => {
717
805
  const formElement = get2().formElement;
@@ -864,12 +952,12 @@ var createFormState = (set, get2) => ({
864
952
  mutateAsArray(
865
953
  fieldName,
866
954
  state.touchedFields,
867
- (array) => insert(array, index, false)
955
+ (array) => insertEmpty(array, index)
868
956
  );
869
957
  mutateAsArray(
870
958
  fieldName,
871
959
  state.fieldErrors,
872
- (array) => insert(array, index, void 0)
960
+ (array) => insertEmpty(array, index)
873
961
  );
874
962
  });
875
963
  get2().controlledFields.kickoffValueUpdate(fieldName);
@@ -921,12 +1009,12 @@ var createFormState = (set, get2) => ({
921
1009
  mutateAsArray(
922
1010
  fieldName,
923
1011
  state.touchedFields,
924
- (array) => array.unshift(false)
1012
+ (array) => insertEmpty(array, 0)
925
1013
  );
926
1014
  mutateAsArray(
927
1015
  fieldName,
928
1016
  state.fieldErrors,
929
- (array) => array.unshift(void 0)
1017
+ (array) => insertEmpty(array, 0)
930
1018
  );
931
1019
  });
932
1020
  },
@@ -1058,8 +1146,8 @@ var useDefaultValuesForForm = (context) => {
1058
1146
  var useHasActiveFormSubmit = ({
1059
1147
  fetcher
1060
1148
  }) => {
1061
- const transition = useTransition();
1062
- const hasActiveSubmission = fetcher ? fetcher.state === "submitting" : !!transition.submission;
1149
+ let navigation = useNavigation();
1150
+ const hasActiveSubmission = fetcher ? fetcher.state === "submitting" : navigation.state === "submitting";
1063
1151
  return hasActiveSubmission;
1064
1152
  };
1065
1153
  var useFieldTouched = (field, { formId }) => {
@@ -1092,7 +1180,7 @@ var useFieldDefaultValue = (name, context) => {
1092
1180
  var useInternalIsSubmitting = (formId) => useFormStore(formId, (state) => state.isSubmitting);
1093
1181
  var useInternalIsValid = (formId) => useFormStore(formId, (state) => state.isValid());
1094
1182
  var useInternalHasBeenSubmitted = (formId) => useFormStore(formId, (state) => state.hasBeenSubmitted);
1095
- var useValidateField = (formId) => useFormStore(formId, (state) => state.validateField);
1183
+ var useSmartValidate = (formId) => useFormStore(formId, (state) => state.smartValidate);
1096
1184
  var useValidate = (formId) => useFormStore(formId, (state) => state.validate);
1097
1185
  var noOpReceiver = () => () => {
1098
1186
  };
@@ -1203,7 +1291,7 @@ var useField = (name, options) => {
1203
1291
  const error = useFieldError(name, formContext);
1204
1292
  const clearError = useClearError(formContext);
1205
1293
  const hasBeenSubmitted = useInternalHasBeenSubmitted(formContext.formId);
1206
- const validateField = useValidateField(formContext.formId);
1294
+ const smartValidate = useSmartValidate(formContext.formId);
1207
1295
  const registerReceiveFocus = useRegisterReceiveFocus(formContext.formId);
1208
1296
  useEffect2(() => {
1209
1297
  if (handleReceiveFocus)
@@ -1213,9 +1301,7 @@ var useField = (name, options) => {
1213
1301
  const helpers = {
1214
1302
  error,
1215
1303
  clearError: () => clearError(name),
1216
- validate: () => {
1217
- validateField(name);
1218
- },
1304
+ validate: () => smartValidate({ alwaysIncludeErrorsFromFields: [name] }),
1219
1305
  defaultValue,
1220
1306
  touched,
1221
1307
  setTouched
@@ -1239,7 +1325,7 @@ var useField = (name, options) => {
1239
1325
  name,
1240
1326
  hasBeenSubmitted,
1241
1327
  options == null ? void 0 : options.validationBehavior,
1242
- validateField
1328
+ smartValidate
1243
1329
  ]);
1244
1330
  return field;
1245
1331
  };
@@ -1461,6 +1547,9 @@ function ValidatedForm({
1461
1547
  method,
1462
1548
  replace: replace2,
1463
1549
  id,
1550
+ preventScrollReset,
1551
+ relative,
1552
+ encType,
1464
1553
  ...rest
1465
1554
  }) {
1466
1555
  var _a;
@@ -1553,11 +1642,11 @@ function ValidatedForm({
1553
1642
  startSubmit();
1554
1643
  const submitter = nativeEvent.submitter;
1555
1644
  const formMethod = (submitter == null ? void 0 : submitter.formMethod) || method;
1556
- const formDataToValidate = getDataFromForm(target);
1645
+ const formData = getDataFromForm(target);
1557
1646
  if (submitter == null ? void 0 : submitter.name) {
1558
- formDataToValidate.append(submitter.name, submitter.value);
1647
+ formData.append(submitter.name, submitter.value);
1559
1648
  }
1560
- const result = await validator.validate(formDataToValidate);
1649
+ const result = await validator.validate(formData);
1561
1650
  if (result.error) {
1562
1651
  setFieldErrors(result.error.fieldErrors);
1563
1652
  endSubmit();
@@ -1576,13 +1665,18 @@ function ValidatedForm({
1576
1665
  endSubmit();
1577
1666
  return;
1578
1667
  }
1668
+ const opts = {
1669
+ method: formMethod,
1670
+ replace: replace2,
1671
+ preventScrollReset,
1672
+ relative,
1673
+ action,
1674
+ encType
1675
+ };
1579
1676
  if (fetcher)
1580
- fetcher.submit(submitter || target, { method: formMethod });
1677
+ fetcher.submit(formData, opts);
1581
1678
  else
1582
- submit(submitter || target, {
1583
- replace: replace2,
1584
- method: formMethod
1585
- });
1679
+ submit(formData, opts);
1586
1680
  }
1587
1681
  };
1588
1682
  return /* @__PURE__ */ jsx(
@@ -1593,7 +1687,10 @@ function ValidatedForm({
1593
1687
  id,
1594
1688
  action,
1595
1689
  method,
1690
+ encType,
1596
1691
  replace: replace2,
1692
+ preventScrollReset,
1693
+ relative,
1597
1694
  onSubmit: (e) => {
1598
1695
  e.preventDefault();
1599
1696
  handleSubmit(
@@ -1711,7 +1808,7 @@ var useFormState = (formId) => {
1711
1808
  var useFormHelpers = (formId) => {
1712
1809
  const formContext = useInternalFormContext(formId, "useFormHelpers");
1713
1810
  const setTouched = useSetTouched(formContext);
1714
- const validateField = useValidateField(formContext.formId);
1811
+ const validateField = useSmartValidate(formContext.formId);
1715
1812
  const validate = useValidate(formContext.formId);
1716
1813
  const clearError = useClearError(formContext);
1717
1814
  const setFieldErrors = useSetFieldErrors(formContext.formId);
@@ -1721,7 +1818,13 @@ var useFormHelpers = (formId) => {
1721
1818
  return useMemo3(
1722
1819
  () => ({
1723
1820
  setTouched,
1724
- validateField,
1821
+ validateField: async (fieldName) => {
1822
+ var _a, _b;
1823
+ const res = await validateField({
1824
+ alwaysIncludeErrorsFromFields: [fieldName]
1825
+ });
1826
+ return (_b = (_a = res.error) == null ? void 0 : _a.fieldErrors[fieldName]) != null ? _b : null;
1827
+ },
1725
1828
  clearError,
1726
1829
  validate,
1727
1830
  clearAllErrors: () => setFieldErrors({}),
@@ -1794,7 +1897,8 @@ var useFormContext = (formId) => {
1794
1897
  };
1795
1898
 
1796
1899
  // src/internal/state/fieldArray.tsx
1797
- import { useMemo as useMemo5 } from "react";
1900
+ import { nanoid } from "nanoid";
1901
+ import { useMemo as useMemo5, useRef as useRef5 } from "react";
1798
1902
  import { useCallback as useCallback6 } from "react";
1799
1903
  import invariant4 from "tiny-invariant";
1800
1904
  import { Fragment as Fragment2, jsx as jsx2 } from "react/jsx-runtime";
@@ -1802,7 +1906,7 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
1802
1906
  const value = useFieldDefaultValue(field, context);
1803
1907
  useRegisterControlledField(context, field);
1804
1908
  const hasBeenSubmitted = useInternalHasBeenSubmitted(context.formId);
1805
- const validateField = useValidateField(context.formId);
1909
+ const validateField = useSmartValidate(context.formId);
1806
1910
  const error = useFieldError(field, context);
1807
1911
  const resolvedValidationBehavior = {
1808
1912
  initial: "onSubmit",
@@ -1812,7 +1916,7 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
1812
1916
  const behavior = hasBeenSubmitted ? resolvedValidationBehavior.whenSubmitted : resolvedValidationBehavior.initial;
1813
1917
  const maybeValidate = useCallback6(() => {
1814
1918
  if (behavior === "onChange") {
1815
- validateField(field);
1919
+ validateField({ alwaysIncludeErrorsFromFields: [field] });
1816
1920
  }
1817
1921
  }, [behavior, field, validateField]);
1818
1922
  invariant4(
@@ -1823,56 +1927,78 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
1823
1927
  context.formId,
1824
1928
  (state) => state.controlledFields.array
1825
1929
  );
1930
+ const arrayValue = useMemo5(() => value != null ? value : [], [value]);
1931
+ const keyRef = useRef5([]);
1932
+ if (keyRef.current.length !== arrayValue.length) {
1933
+ keyRef.current = arrayValue.map(() => nanoid());
1934
+ }
1826
1935
  const helpers = useMemo5(
1827
1936
  () => ({
1828
1937
  push: (item) => {
1829
1938
  arr.push(field, item);
1939
+ keyRef.current.push(nanoid());
1830
1940
  maybeValidate();
1831
1941
  },
1832
1942
  swap: (indexA, indexB) => {
1833
1943
  arr.swap(field, indexA, indexB);
1944
+ swap(keyRef.current, indexA, indexB);
1834
1945
  maybeValidate();
1835
1946
  },
1836
1947
  move: (from2, to) => {
1837
1948
  arr.move(field, from2, to);
1949
+ move(keyRef.current, from2, to);
1838
1950
  maybeValidate();
1839
1951
  },
1840
1952
  insert: (index, value2) => {
1841
1953
  arr.insert(field, index, value2);
1954
+ insert(keyRef.current, index, nanoid());
1842
1955
  maybeValidate();
1843
1956
  },
1844
1957
  unshift: (value2) => {
1845
1958
  arr.unshift(field, value2);
1959
+ keyRef.current.unshift(nanoid());
1846
1960
  maybeValidate();
1847
1961
  },
1848
1962
  remove: (index) => {
1849
1963
  arr.remove(field, index);
1964
+ remove(keyRef.current, index);
1850
1965
  maybeValidate();
1851
1966
  },
1852
1967
  pop: () => {
1853
1968
  arr.pop(field);
1969
+ keyRef.current.pop();
1854
1970
  maybeValidate();
1855
1971
  },
1856
1972
  replace: (index, value2) => {
1857
1973
  arr.replace(field, index, value2);
1974
+ keyRef.current[index] = nanoid();
1858
1975
  maybeValidate();
1859
1976
  }
1860
1977
  }),
1861
1978
  [arr, field, maybeValidate]
1862
1979
  );
1863
- const arrayValue = useMemo5(() => value != null ? value : [], [value]);
1864
- return [arrayValue, helpers, error];
1980
+ const valueWithKeys = useMemo5(() => {
1981
+ const result = [];
1982
+ arrayValue.forEach((item, index) => {
1983
+ result[index] = {
1984
+ key: keyRef.current[index],
1985
+ defaultValue: item
1986
+ };
1987
+ });
1988
+ return result;
1989
+ }, [arrayValue]);
1990
+ return [valueWithKeys, helpers, error];
1865
1991
  };
1866
1992
  function useFieldArray(name, { formId, validationBehavior } = {}) {
1867
1993
  const context = useInternalFormContext(formId, "FieldArray");
1868
1994
  return useInternalFieldArray(context, name, validationBehavior);
1869
1995
  }
1870
- var FieldArray = ({
1996
+ function FieldArray({
1871
1997
  name,
1872
1998
  children,
1873
1999
  formId,
1874
2000
  validationBehavior
1875
- }) => {
2001
+ }) {
1876
2002
  const context = useInternalFormContext(formId, "FieldArray");
1877
2003
  const [value, helpers, error] = useInternalFieldArray(
1878
2004
  context,
@@ -1880,7 +2006,7 @@ var FieldArray = ({
1880
2006
  validationBehavior
1881
2007
  );
1882
2008
  return /* @__PURE__ */ jsx2(Fragment2, { children: children(value, helpers, error) });
1883
- };
2009
+ }
1884
2010
  export {
1885
2011
  FieldArray,
1886
2012
  ValidatedForm,