remix-validated-form 4.5.4 → 4.6.0-beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/browser/ValidatedForm.js +0 -3
  3. package/browser/index.d.ts +1 -0
  4. package/browser/index.js +1 -0
  5. package/browser/internal/hooks.d.ts +1 -0
  6. package/browser/internal/hooks.js +3 -4
  7. package/browser/internal/state/controlledFields.d.ts +1 -0
  8. package/browser/internal/state/controlledFields.js +17 -29
  9. package/browser/internal/state/createFormStore.d.ts +31 -1
  10. package/browser/internal/state/createFormStore.js +175 -8
  11. package/dist/remix-validated-form.cjs.js +4 -4
  12. package/dist/remix-validated-form.cjs.js.map +1 -1
  13. package/dist/remix-validated-form.es.js +336 -126
  14. package/dist/remix-validated-form.es.js.map +1 -1
  15. package/dist/remix-validated-form.umd.js +4 -4
  16. package/dist/remix-validated-form.umd.js.map +1 -1
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/internal/hooks.d.ts +1 -0
  19. package/dist/types/internal/state/arrayUtil.d.ts +12 -0
  20. package/dist/types/internal/state/controlledFields.d.ts +1 -0
  21. package/dist/types/internal/state/createFormStore.d.ts +31 -1
  22. package/dist/types/internal/state/fieldArray.d.ts +28 -0
  23. package/package.json +1 -1
  24. package/src/ValidatedForm.tsx +0 -3
  25. package/src/index.ts +6 -0
  26. package/src/internal/hooks.ts +9 -4
  27. package/src/internal/logic/nestedObjectToPathObject.ts +63 -0
  28. package/src/internal/state/arrayUtil.ts +399 -0
  29. package/src/internal/state/controlledFields.ts +39 -43
  30. package/src/internal/state/createFormStore.ts +286 -10
  31. package/src/internal/state/fieldArray.tsx +155 -0
  32. package/dist/types/internal/state/controlledFieldStore.d.ts +0 -26
  33. package/src/internal/state/controlledFieldStore.ts +0 -112
@@ -1,17 +1,17 @@
1
1
  $ vite build
2
2
  vite v2.9.5 building for production...
3
3
  transforming...
4
- ✓ 482 modules transformed.
4
+ ✓ 483 modules transformed.
5
5
  rendering chunks...
6
- dist/remix-validated-form.cjs.js  45.80 KiB / gzip: 17.20 KiB
7
- dist/remix-validated-form.cjs.js.map 254.77 KiB
8
- dist/remix-validated-form.es.js  101.57 KiB / gzip: 23.90 KiB
9
- dist/remix-validated-form.es.js.map 262.81 KiB
10
- dist/remix-validated-form.umd.js  46.00 KiB / gzip: 17.31 KiB
11
- dist/remix-validated-form.umd.js.map 254.75 KiB
6
+ dist/remix-validated-form.cjs.js  49.71 KiB / gzip: 18.12 KiB
7
+ dist/remix-validated-form.cjs.js.map 281.88 KiB
8
+ dist/remix-validated-form.es.js  109.95 KiB / gzip: 25.38 KiB
9
+ dist/remix-validated-form.es.js.map 290.49 KiB
10
+ dist/remix-validated-form.umd.js  49.91 KiB / gzip: 18.22 KiB
11
+ dist/remix-validated-form.umd.js.map 281.85 KiB
12
12
  
13
13
  [vite:dts] Start generate declaration files...
14
- [vite:dts] Declaration files built in 2738ms.
14
+ [vite:dts] Declaration files built in 2756ms.
15
15
  
16
16
  No name was provided for external module 'react' in output.globals – guessing 'React'
17
17
  No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
@@ -7,7 +7,6 @@ import { FORM_ID_FIELD } from "./internal/constants";
7
7
  import { InternalFormContext, } from "./internal/formContext";
8
8
  import { useDefaultValuesFromLoader, useErrorResponseForForm, useHasActiveFormSubmit, useSetFieldErrors, } from "./internal/hooks";
9
9
  import { useMultiValueMap } from "./internal/MultiValueMap";
10
- import { useControlledFieldStore } from "./internal/state/controlledFieldStore";
11
10
  import { useRootFormStore, } from "./internal/state/createFormStore";
12
11
  import { useFormStore } from "./internal/state/storeHooks";
13
12
  import { useSubmitComplete } from "./internal/submissionCallbacks";
@@ -115,7 +114,6 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
115
114
  const setFieldErrors = useSetFieldErrors(formId);
116
115
  const setFieldError = useFormStore(formId, (state) => state.setFieldError);
117
116
  const reset = useFormStore(formId, (state) => state.reset);
118
- const resetControlledFields = useControlledFieldStore((state) => state.reset);
119
117
  const startSubmit = useFormStore(formId, (state) => state.startSubmit);
120
118
  const endSubmit = useFormStore(formId, (state) => state.endSubmit);
121
119
  const syncFormProps = useFormStore(formId, (state) => state.syncFormProps);
@@ -200,6 +198,5 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
200
198
  if (event.defaultPrevented)
201
199
  return;
202
200
  reset();
203
- resetControlledFields(formId);
204
201
  }, children: _jsx(InternalFormContext.Provider, { value: contextValue, children: _jsxs(_Fragment, { children: [_jsx(FormResetter, { formRef: formRef, resetAfterSubmit: resetAfterSubmit }, void 0), subaction && (_jsx("input", { type: "hidden", value: subaction, name: "subaction" }, void 0)), id && _jsx("input", { type: "hidden", value: id, name: FORM_ID_FIELD }, void 0), children] }, void 0) }, void 0) }, void 0));
205
202
  }
@@ -4,3 +4,4 @@ export * from "./ValidatedForm";
4
4
  export * from "./validation/types";
5
5
  export * from "./validation/createValidator";
6
6
  export * from "./userFacingFormContext";
7
+ export { FieldArray, useFieldArray, type FieldArrayProps, type FieldArrayHelpers, } from "./internal/state/fieldArray";
package/browser/index.js CHANGED
@@ -4,3 +4,4 @@ export * from "./ValidatedForm";
4
4
  export * from "./validation/types";
5
5
  export * from "./validation/createValidator";
6
6
  export * from "./userFacingFormContext";
7
+ export { FieldArray, useFieldArray, } from "./internal/state/fieldArray";
@@ -13,6 +13,7 @@ export declare const useHasActiveFormSubmit: ({ fetcher, }: InternalFormContextV
13
13
  export declare const useFieldTouched: (field: string, { formId }: InternalFormContextValue) => readonly [boolean, (touched: boolean) => void];
14
14
  export declare const useFieldError: (name: string, context: InternalFormContextValue) => string | undefined;
15
15
  export declare const useClearError: (context: InternalFormContextValue) => (field: string) => void;
16
+ export declare const useCurrentDefaultValueForField: (formId: InternalFormId, field: string) => any;
16
17
  export declare const useFieldDefaultValue: (name: string, context: InternalFormContextValue) => any;
17
18
  export declare const useInternalIsSubmitting: (formId: InternalFormId) => boolean;
18
19
  export declare const useInternalIsValid: (formId: InternalFormId) => boolean;
@@ -92,12 +92,11 @@ export const useClearError = (context) => {
92
92
  const { formId } = context;
93
93
  return useFormStore(formId, (state) => state.clearFieldError);
94
94
  };
95
+ export const useCurrentDefaultValueForField = (formId, field) => useFormStore(formId, (state) => lodashGet(state.currentDefaultValues, field));
95
96
  export const useFieldDefaultValue = (name, context) => {
96
97
  const defaultValues = useDefaultValuesForForm(context);
97
- const state = useSyncedDefaultValues(context.formId);
98
- return defaultValues
99
- .map((val) => lodashGet(val, name))
100
- .hydrateTo(lodashGet(state, name));
98
+ const state = useCurrentDefaultValueForField(context.formId, name);
99
+ return defaultValues.map((val) => lodashGet(val, name)).hydrateTo(state);
101
100
  };
102
101
  export const useInternalIsSubmitting = (formId) => useFormStore(formId, (state) => state.isSubmitting);
103
102
  export const useInternalIsValid = (formId) => useFormStore(formId, (state) => state.isValid());
@@ -1,6 +1,7 @@
1
1
  import { InternalFormContextValue } from "../formContext";
2
2
  import { InternalFormId } from "./types";
3
3
  export declare const useControlledFieldValue: (context: InternalFormContextValue, field: string) => any;
4
+ export declare const useRegisterControlledField: (context: InternalFormContextValue, field: string) => void;
4
5
  export declare const useControllableValue: (context: InternalFormContextValue, field: string) => readonly [any, (value: unknown) => void];
5
6
  export declare const useUpdateControllableValue: (formId: InternalFormId) => (field: string, value: unknown) => void;
6
7
  export declare const useAwaitValue: (formId: InternalFormId) => (field: string) => Promise<void>;
@@ -1,48 +1,36 @@
1
1
  import { useCallback, useEffect } from "react";
2
2
  import { useFieldDefaultValue } from "../hooks";
3
- import { useControlledFieldStore } from "./controlledFieldStore";
4
3
  import { useFormStore } from "./storeHooks";
5
4
  export const useControlledFieldValue = (context, field) => {
6
- const value = useControlledFieldStore((state) => { var _a; return (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.value; });
5
+ const value = useFormStore(context.formId, (state) => state.controlledFields.getValue(field));
7
6
  const isFormHydrated = useFormStore(context.formId, (state) => state.isHydrated);
8
7
  const defaultValue = useFieldDefaultValue(field, context);
9
- const isFieldHydrated = useControlledFieldStore((state) => { var _a, _b; return (_b = (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.hydrated) !== null && _b !== void 0 ? _b : false; });
10
- const hydrateWithDefault = useControlledFieldStore((state) => state.hydrateWithDefault);
11
- useEffect(() => {
12
- if (isFormHydrated && !isFieldHydrated) {
13
- hydrateWithDefault(context.formId, field, defaultValue);
14
- }
15
- }, [
16
- context.formId,
17
- defaultValue,
18
- field,
19
- hydrateWithDefault,
20
- isFieldHydrated,
21
- isFormHydrated,
22
- ]);
23
- return isFieldHydrated ? value : defaultValue;
8
+ return isFormHydrated ? value : defaultValue;
24
9
  };
25
- export const useControllableValue = (context, field) => {
26
- const resolveUpdate = useControlledFieldStore((state) => { var _a; return (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.resolveValueUpdate; });
10
+ export const useRegisterControlledField = (context, field) => {
11
+ const resolveUpdate = useFormStore(context.formId, (state) => state.controlledFields.valueUpdateResolvers[field]);
27
12
  useEffect(() => {
28
13
  resolveUpdate === null || resolveUpdate === void 0 ? void 0 : resolveUpdate();
29
14
  }, [resolveUpdate]);
30
- const register = useControlledFieldStore((state) => state.register);
31
- const unregister = useControlledFieldStore((state) => state.unregister);
15
+ const register = useFormStore(context.formId, (state) => state.controlledFields.register);
16
+ const unregister = useFormStore(context.formId, (state) => state.controlledFields.unregister);
32
17
  useEffect(() => {
33
- register(context.formId, field);
34
- return () => unregister(context.formId, field);
18
+ register(field);
19
+ return () => unregister(field);
35
20
  }, [context.formId, field, register, unregister]);
36
- const setControlledFieldValue = useControlledFieldStore((state) => state.setValue);
37
- const setValue = useCallback((value) => setControlledFieldValue(context.formId, field, value), [context.formId, field, setControlledFieldValue]);
21
+ };
22
+ export const useControllableValue = (context, field) => {
23
+ useRegisterControlledField(context, field);
24
+ const setControlledFieldValue = useFormStore(context.formId, (state) => state.controlledFields.setValue);
25
+ const setValue = useCallback((value) => setControlledFieldValue(field, value), [field, setControlledFieldValue]);
38
26
  const value = useControlledFieldValue(context, field);
39
27
  return [value, setValue];
40
28
  };
41
29
  export const useUpdateControllableValue = (formId) => {
42
- const setValue = useControlledFieldStore((state) => state.setValue);
43
- return useCallback((field, value) => setValue(formId, field, value), [formId, setValue]);
30
+ const setValue = useFormStore(formId, (state) => state.controlledFields.setValue);
31
+ return useCallback((field, value) => setValue(field, value), [setValue]);
44
32
  };
45
33
  export const useAwaitValue = (formId) => {
46
- const awaitValue = useControlledFieldStore((state) => state.awaitValueUpdate);
47
- return useCallback((field) => awaitValue(formId, field), [awaitValue, formId]);
34
+ const awaitValue = useFormStore(formId, (state) => state.controlledFields.awaitValueUpdate);
35
+ return useCallback((field) => awaitValue(field), [awaitValue]);
48
36
  };
@@ -27,6 +27,7 @@ export declare type FormState = {
27
27
  touchedFields: TouchedFields;
28
28
  formProps?: SyncedFormProps;
29
29
  formElement: HTMLFormElement | null;
30
+ currentDefaultValues: Record<string, any>;
30
31
  isValid: () => boolean;
31
32
  startSubmit: () => void;
32
33
  endSubmit: () => void;
@@ -36,13 +37,42 @@ export declare type FormState = {
36
37
  clearFieldError: (field: string) => void;
37
38
  reset: () => void;
38
39
  syncFormProps: (props: SyncedFormProps) => void;
39
- setHydrated: () => void;
40
40
  setFormElement: (formElement: HTMLFormElement | null) => void;
41
41
  validateField: (fieldName: string) => Promise<string | null>;
42
42
  validate: () => Promise<ValidationResult<unknown>>;
43
43
  resetFormElement: () => void;
44
44
  submit: () => void;
45
45
  getValues: () => FormData;
46
+ controlledFields: {
47
+ values: {
48
+ [fieldName: string]: any;
49
+ };
50
+ refCounts: {
51
+ [fieldName: string]: number;
52
+ };
53
+ valueUpdatePromises: {
54
+ [fieldName: string]: Promise<void>;
55
+ };
56
+ valueUpdateResolvers: {
57
+ [fieldName: string]: () => void;
58
+ };
59
+ register: (fieldName: string) => void;
60
+ unregister: (fieldName: string) => void;
61
+ setValue: (fieldName: string, value: unknown) => void;
62
+ kickoffValueUpdate: (fieldName: string) => void;
63
+ getValue: (fieldName: string) => unknown;
64
+ awaitValueUpdate: (fieldName: string) => Promise<void>;
65
+ array: {
66
+ push: (fieldName: string, value: unknown) => void;
67
+ swap: (fieldName: string, indexA: number, indexB: number) => void;
68
+ move: (fieldName: string, fromIndex: number, toIndex: number) => void;
69
+ insert: (fieldName: string, index: number, value: unknown) => void;
70
+ unshift: (fieldName: string, value: unknown) => void;
71
+ remove: (fieldName: string, index: number) => void;
72
+ pop: (fieldName: string) => void;
73
+ replace: (fieldName: string, index: number, value: unknown) => void;
74
+ };
75
+ };
46
76
  };
47
77
  export declare const useRootFormStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormStoreState>, "setState"> & {
48
78
  setState(nextStateOrUpdater: FormStoreState | Partial<FormStoreState> | ((state: WritableDraft<FormStoreState>) => void), shouldReplace?: boolean | undefined): void;
@@ -1,7 +1,9 @@
1
+ import lodashGet from "lodash/get";
2
+ import lodashSet from "lodash/set";
1
3
  import invariant from "tiny-invariant";
2
4
  import create from "zustand";
3
5
  import { immer } from "zustand/middleware/immer";
4
- import { useControlledFieldStore } from "./controlledFieldStore";
6
+ import * as arrayUtil from "./arrayUtil";
5
7
  const noOp = () => { };
6
8
  const defaultFormState = {
7
9
  isHydrated: false,
@@ -17,9 +19,9 @@ const defaultFormState = {
17
19
  setFieldError: noOp,
18
20
  setFieldErrors: noOp,
19
21
  clearFieldError: noOp,
22
+ currentDefaultValues: {},
20
23
  reset: () => noOp,
21
24
  syncFormProps: noOp,
22
- setHydrated: noOp,
23
25
  setFormElement: noOp,
24
26
  validateField: async () => null,
25
27
  validate: async () => {
@@ -30,8 +32,32 @@ const defaultFormState = {
30
32
  },
31
33
  resetFormElement: noOp,
32
34
  getValues: () => new FormData(),
35
+ controlledFields: {
36
+ values: {},
37
+ refCounts: {},
38
+ valueUpdatePromises: {},
39
+ valueUpdateResolvers: {},
40
+ register: noOp,
41
+ unregister: noOp,
42
+ setValue: noOp,
43
+ getValue: noOp,
44
+ kickoffValueUpdate: noOp,
45
+ awaitValueUpdate: async () => {
46
+ throw new Error("AwaitValueUpdate called before form was initialized.");
47
+ },
48
+ array: {
49
+ push: noOp,
50
+ swap: noOp,
51
+ move: noOp,
52
+ insert: noOp,
53
+ unshift: noOp,
54
+ remove: noOp,
55
+ pop: noOp,
56
+ replace: noOp,
57
+ },
58
+ },
33
59
  };
34
- const createFormState = (formId, set, get) => ({
60
+ const createFormState = (set, get) => ({
35
61
  // It's not "hydrated" until the form props are synced
36
62
  isHydrated: false,
37
63
  isSubmitting: false,
@@ -39,6 +65,7 @@ const createFormState = (formId, set, get) => ({
39
65
  touchedFields: {},
40
66
  fieldErrors: {},
41
67
  formElement: null,
68
+ currentDefaultValues: {},
42
69
  isValid: () => Object.keys(get().fieldErrors).length === 0,
43
70
  startSubmit: () => set((state) => {
44
71
  state.isSubmitting = true;
@@ -60,17 +87,22 @@ const createFormState = (formId, set, get) => ({
60
87
  delete state.fieldErrors[fieldName];
61
88
  }),
62
89
  reset: () => set((state) => {
90
+ var _a, _b;
63
91
  state.fieldErrors = {};
64
92
  state.touchedFields = {};
65
93
  state.hasBeenSubmitted = false;
94
+ const nextDefaults = (_b = (_a = state.formProps) === null || _a === void 0 ? void 0 : _a.defaultValues) !== null && _b !== void 0 ? _b : {};
95
+ state.controlledFields.values = nextDefaults;
96
+ state.currentDefaultValues = nextDefaults;
66
97
  }),
67
98
  syncFormProps: (props) => set((state) => {
99
+ if (!state.isHydrated) {
100
+ state.controlledFields.values = props.defaultValues;
101
+ state.currentDefaultValues = props.defaultValues;
102
+ }
68
103
  state.formProps = props;
69
104
  state.isHydrated = true;
70
105
  }),
71
- setHydrated: () => set((state) => {
72
- state.isHydrated = true;
73
- }),
74
106
  setFormElement: (formElement) => {
75
107
  // This gets called frequently, so we want to avoid calling set() every time
76
108
  // Or else we wind up with an infinite loop
@@ -88,7 +120,7 @@ const createFormState = (formId, set, get) => ({
88
120
  invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
89
121
  const validator = (_a = get().formProps) === null || _a === void 0 ? void 0 : _a.validator;
90
122
  invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
91
- await ((_c = (_b = useControlledFieldStore.getState()).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, formId, field));
123
+ await ((_c = (_b = get().controlledFields).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, field));
92
124
  const { error } = await validator.validateField(new FormData(formElement), field);
93
125
  if (error) {
94
126
  get().setFieldError(field, error);
@@ -117,6 +149,141 @@ const createFormState = (formId, set, get) => ({
117
149
  },
118
150
  getValues: () => { var _a; return new FormData((_a = get().formElement) !== null && _a !== void 0 ? _a : undefined); },
119
151
  resetFormElement: () => { var _a; return (_a = get().formElement) === null || _a === void 0 ? void 0 : _a.reset(); },
152
+ controlledFields: {
153
+ values: {},
154
+ refCounts: {},
155
+ valueUpdatePromises: {},
156
+ valueUpdateResolvers: {},
157
+ register: (fieldName) => {
158
+ set((state) => {
159
+ var _a;
160
+ const current = (_a = state.controlledFields.refCounts[fieldName]) !== null && _a !== void 0 ? _a : 0;
161
+ state.controlledFields.refCounts[fieldName] = current + 1;
162
+ });
163
+ },
164
+ unregister: (fieldName) => {
165
+ // For this helper in particular, we may run into a case where state is undefined.
166
+ // When the whole form unmounts, the form state may be cleaned up before the fields are.
167
+ if (get() === null || get() === undefined)
168
+ return;
169
+ set((state) => {
170
+ var _a, _b, _c;
171
+ const current = (_a = state.controlledFields.refCounts[fieldName]) !== null && _a !== void 0 ? _a : 0;
172
+ if (current > 1) {
173
+ state.controlledFields.refCounts[fieldName] = current - 1;
174
+ return;
175
+ }
176
+ const isNested = Object.keys(state.controlledFields.refCounts).some((key) => fieldName.startsWith(key) && key !== fieldName);
177
+ // When nested within a field array, we should leave resetting up to the field array
178
+ if (!isNested) {
179
+ lodashSet(state.controlledFields.values, fieldName, lodashGet((_b = state.formProps) === null || _b === void 0 ? void 0 : _b.defaultValues, fieldName));
180
+ lodashSet(state.currentDefaultValues, fieldName, lodashGet((_c = state.formProps) === null || _c === void 0 ? void 0 : _c.defaultValues, fieldName));
181
+ }
182
+ delete state.controlledFields.refCounts[fieldName];
183
+ });
184
+ },
185
+ getValue: (fieldName) => lodashGet(get().controlledFields.values, fieldName),
186
+ setValue: (fieldName, value) => {
187
+ set((state) => {
188
+ lodashSet(state.controlledFields.values, fieldName, value);
189
+ });
190
+ get().controlledFields.kickoffValueUpdate(fieldName);
191
+ },
192
+ kickoffValueUpdate: (fieldName) => {
193
+ const clear = () => set((state) => {
194
+ delete state.controlledFields.valueUpdateResolvers[fieldName];
195
+ delete state.controlledFields.valueUpdatePromises[fieldName];
196
+ });
197
+ set((state) => {
198
+ const promise = new Promise((resolve) => {
199
+ state.controlledFields.valueUpdateResolvers[fieldName] = resolve;
200
+ }).then(clear);
201
+ state.controlledFields.valueUpdatePromises[fieldName] = promise;
202
+ });
203
+ },
204
+ awaitValueUpdate: async (fieldName) => {
205
+ await get().controlledFields.valueUpdatePromises[fieldName];
206
+ },
207
+ array: {
208
+ push: (fieldName, item) => {
209
+ set((state) => {
210
+ arrayUtil
211
+ .getArray(state.controlledFields.values, fieldName)
212
+ .push(item);
213
+ arrayUtil.getArray(state.currentDefaultValues, fieldName).push(item);
214
+ // New item added to the end, no need to update touched or error
215
+ });
216
+ get().controlledFields.kickoffValueUpdate(fieldName);
217
+ },
218
+ swap: (fieldName, indexA, indexB) => {
219
+ set((state) => {
220
+ arrayUtil.swap(arrayUtil.getArray(state.controlledFields.values, fieldName), indexA, indexB);
221
+ arrayUtil.swap(arrayUtil.getArray(state.currentDefaultValues, fieldName), indexA, indexB);
222
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.swap(array, indexA, indexB));
223
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.swap(array, indexA, indexB));
224
+ });
225
+ get().controlledFields.kickoffValueUpdate(fieldName);
226
+ },
227
+ move: (fieldName, from, to) => {
228
+ set((state) => {
229
+ arrayUtil.move(arrayUtil.getArray(state.controlledFields.values, fieldName), from, to);
230
+ arrayUtil.move(arrayUtil.getArray(state.currentDefaultValues, fieldName), from, to);
231
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.move(array, from, to));
232
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.move(array, from, to));
233
+ });
234
+ get().controlledFields.kickoffValueUpdate(fieldName);
235
+ },
236
+ insert: (fieldName, index, item) => {
237
+ set((state) => {
238
+ arrayUtil.insert(arrayUtil.getArray(state.controlledFields.values, fieldName), index, item);
239
+ arrayUtil.insert(arrayUtil.getArray(state.currentDefaultValues, fieldName), index, item);
240
+ // Even though this is a new item, we need to push around other items.
241
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.insert(array, index, false));
242
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.insert(array, index, undefined));
243
+ });
244
+ get().controlledFields.kickoffValueUpdate(fieldName);
245
+ },
246
+ remove: (fieldName, index) => {
247
+ set((state) => {
248
+ arrayUtil.remove(arrayUtil.getArray(state.controlledFields.values, fieldName), index);
249
+ arrayUtil.remove(arrayUtil.getArray(state.currentDefaultValues, fieldName), index);
250
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.remove(array, index));
251
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.remove(array, index));
252
+ });
253
+ get().controlledFields.kickoffValueUpdate(fieldName);
254
+ },
255
+ pop: (fieldName) => {
256
+ set((state) => {
257
+ arrayUtil.getArray(state.controlledFields.values, fieldName).pop();
258
+ arrayUtil.getArray(state.currentDefaultValues, fieldName).pop();
259
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => array.pop());
260
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => array.pop());
261
+ });
262
+ get().controlledFields.kickoffValueUpdate(fieldName);
263
+ },
264
+ unshift: (fieldName, value) => {
265
+ set((state) => {
266
+ arrayUtil
267
+ .getArray(state.controlledFields.values, fieldName)
268
+ .unshift(value);
269
+ arrayUtil
270
+ .getArray(state.currentDefaultValues, fieldName)
271
+ .unshift(value);
272
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => array.unshift(false));
273
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => array.unshift(undefined));
274
+ });
275
+ },
276
+ replace: (fieldName, index, item) => {
277
+ set((state) => {
278
+ arrayUtil.replace(arrayUtil.getArray(state.controlledFields.values, fieldName), index, item);
279
+ arrayUtil.replace(arrayUtil.getArray(state.currentDefaultValues, fieldName), index, item);
280
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.replace(array, index, item));
281
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.replace(array, index, item));
282
+ });
283
+ get().controlledFields.kickoffValueUpdate(fieldName);
284
+ },
285
+ },
286
+ },
120
287
  });
121
288
  export const useRootFormStore = create()(immer((set, get) => ({
122
289
  forms: {},
@@ -133,7 +300,7 @@ export const useRootFormStore = create()(immer((set, get) => ({
133
300
  if (get().forms[formId])
134
301
  return;
135
302
  set((state) => {
136
- state.forms[formId] = createFormState(formId, (setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
303
+ state.forms[formId] = createFormState((setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
137
304
  });
138
305
  },
139
306
  })));