remix-validated-form 4.4.1 → 4.5.0-beta.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 (42) hide show
  1. package/.turbo/turbo-build.log +8 -5
  2. package/browser/ValidatedForm.js +21 -15
  3. package/browser/hooks.d.ts +1 -1
  4. package/browser/internal/hooks.d.ts +1 -1
  5. package/browser/internal/state/controlledFieldStore.d.ts +23 -21
  6. package/browser/internal/state/controlledFieldStore.js +32 -19
  7. package/browser/internal/state/controlledFields.d.ts +3 -3
  8. package/browser/internal/state/controlledFields.js +19 -21
  9. package/browser/internal/state/createFormStore.d.ts +13 -6
  10. package/browser/internal/state/createFormStore.js +48 -5
  11. package/browser/internal/state/storeHooks.d.ts +1 -3
  12. package/browser/internal/state/storeHooks.js +2 -8
  13. package/browser/internal/state/types.d.ts +1 -0
  14. package/browser/internal/state/types.js +1 -0
  15. package/browser/userFacingFormContext.d.ts +7 -0
  16. package/browser/userFacingFormContext.js +14 -4
  17. package/dist/remix-validated-form.cjs.js +4 -3
  18. package/dist/remix-validated-form.cjs.js.map +1 -0
  19. package/dist/remix-validated-form.es.js +136 -86
  20. package/dist/remix-validated-form.es.js.map +1 -0
  21. package/dist/remix-validated-form.umd.js +4 -3
  22. package/dist/remix-validated-form.umd.js.map +1 -0
  23. package/dist/types/hooks.d.ts +1 -1
  24. package/dist/types/internal/hooks.d.ts +1 -1
  25. package/dist/types/internal/state/controlledFieldStore.d.ts +23 -21
  26. package/dist/types/internal/state/controlledFields.d.ts +3 -3
  27. package/dist/types/internal/state/createFormStore.d.ts +13 -6
  28. package/dist/types/internal/state/storeHooks.d.ts +1 -3
  29. package/dist/types/internal/state/types.d.ts +1 -0
  30. package/package.json +4 -4
  31. package/src/ValidatedForm.tsx +34 -23
  32. package/src/internal/hooks.ts +1 -1
  33. package/src/internal/state/controlledFieldStore.ts +95 -74
  34. package/src/internal/state/controlledFields.ts +38 -26
  35. package/src/internal/state/createFormStore.ts +174 -113
  36. package/src/internal/state/storeHooks.ts +3 -16
  37. package/src/internal/state/types.ts +1 -0
  38. package/src/userFacingFormContext.ts +23 -11
  39. package/dist/types/internal/state/cleanup.d.ts +0 -2
  40. package/dist/types/internal/state/storeFamily.d.ts +0 -9
  41. package/src/internal/state/cleanup.ts +0 -8
  42. package/src/internal/state/storeFamily.ts +0 -24
@@ -64,4 +64,4 @@ export declare const useField: (name: string, options?: {
64
64
  formId?: string | undefined;
65
65
  } | undefined) => FieldProps;
66
66
  export declare const useControlField: <T>(name: string, formId?: string | undefined) => readonly [T, (value: T) => void];
67
- export declare const useUpdateControlledField: (formId?: string | undefined) => (fieldName: string, value: unknown) => void;
67
+ export declare const useUpdateControlledField: (formId?: string | undefined) => (field: string, value: unknown) => void;
@@ -1,7 +1,7 @@
1
1
  import { FieldErrors, ValidationErrorResponseData } from "..";
2
2
  import { InternalFormContextValue } from "./formContext";
3
3
  import { Hydratable } from "./hydratable";
4
- import { InternalFormId } from "./state/storeFamily";
4
+ import { InternalFormId } from "./state/types";
5
5
  export declare const useInternalFormContext: (formId?: string | symbol | undefined, hookName?: string | undefined) => InternalFormContextValue;
6
6
  export declare function useErrorResponseForForm({ fetcher, subaction, formId, }: InternalFormContextValue): ValidationErrorResponseData | null;
7
7
  export declare const useFieldErrorsForForm: (context: InternalFormContextValue) => Hydratable<FieldErrors | undefined>;
@@ -1,24 +1,26 @@
1
+ import { InternalFormId } from "./types";
2
+ export declare type FieldState = {
3
+ refCount: number;
4
+ value: unknown;
5
+ defaultValue?: unknown;
6
+ hydrated: boolean;
7
+ valueUpdatePromise: Promise<void> | undefined;
8
+ resolveValueUpdate: (() => void) | undefined;
9
+ };
1
10
  export declare type ControlledFieldState = {
2
- fields: {
3
- [fieldName: string]: {
4
- refCount: number;
5
- value: unknown;
6
- defaultValue?: unknown;
7
- hydrated: boolean;
8
- valueUpdatePromise: Promise<void> | undefined;
9
- resolveValueUpdate: (() => void) | undefined;
10
- } | undefined;
11
+ forms: {
12
+ [formId: InternalFormId]: {
13
+ [fieldName: string]: FieldState | undefined;
14
+ };
11
15
  };
12
- register: (fieldName: string) => void;
13
- unregister: (fieldName: string) => void;
14
- setValue: (fieldName: string, value: unknown) => void;
15
- hydrateWithDefault: (fieldName: string, defaultValue: unknown) => void;
16
- awaitValueUpdate: (fieldName: string) => Promise<void>;
17
- reset: () => void;
18
- };
19
- export declare const controlledFieldStore: {
20
- (formId: import("./storeFamily").InternalFormId): import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ControlledFieldState>, "setState"> & {
21
- setState(nextStateOrUpdater: ControlledFieldState | Partial<ControlledFieldState> | ((state: import("immer/dist/internal").WritableDraft<ControlledFieldState>) => void), shouldReplace?: boolean | undefined): void;
22
- }>;
23
- remove(formId: import("./storeFamily").InternalFormId): void;
16
+ register: (formId: InternalFormId, fieldName: string) => void;
17
+ unregister: (formId: InternalFormId, fieldName: string) => void;
18
+ getField: (formId: InternalFormId, fieldName: string) => FieldState | undefined;
19
+ setValue: (formId: InternalFormId, fieldName: string, value: unknown) => void;
20
+ hydrateWithDefault: (formId: InternalFormId, fieldName: string, defaultValue: unknown) => void;
21
+ awaitValueUpdate: (formId: InternalFormId, fieldName: string) => Promise<void>;
22
+ reset: (formId: InternalFormId) => void;
24
23
  };
24
+ export declare const useControlledFieldStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ControlledFieldState>, "setState"> & {
25
+ setState(nextStateOrUpdater: ControlledFieldState | Partial<ControlledFieldState> | ((state: import("immer/dist/internal").WritableDraft<ControlledFieldState>) => void), shouldReplace?: boolean | undefined): void;
26
+ }>;
@@ -1,6 +1,6 @@
1
1
  import { InternalFormContextValue } from "../formContext";
2
- import { InternalFormId } from "./storeFamily";
2
+ import { InternalFormId } from "./types";
3
3
  export declare const useControlledFieldValue: (context: InternalFormContextValue, field: string) => any;
4
4
  export declare const useControllableValue: (context: InternalFormContextValue, field: string) => readonly [any, (value: unknown) => void];
5
- export declare const useUpdateControllableValue: (formId: InternalFormId) => (fieldName: string, value: unknown) => void;
6
- export declare const useAwaitValue: (formId: InternalFormId) => (fieldName: string) => Promise<void>;
5
+ export declare const useUpdateControllableValue: (formId: InternalFormId) => (field: string, value: unknown) => void;
6
+ export declare const useAwaitValue: (formId: InternalFormId) => (field: string) => Promise<void>;
@@ -1,4 +1,6 @@
1
+ import { WritableDraft } from "immer/dist/internal";
1
2
  import { FieldErrors, TouchedFields, Validator } from "../../validation/types";
3
+ import { InternalFormId } from "./types";
2
4
  export declare type SyncedFormProps = {
3
5
  formId?: string;
4
6
  action?: string;
@@ -9,6 +11,14 @@ export declare type SyncedFormProps = {
9
11
  registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
10
12
  validator: Validator<unknown>;
11
13
  };
14
+ export declare type FormStoreState = {
15
+ forms: {
16
+ [formId: InternalFormId]: FormState;
17
+ };
18
+ form: (formId: InternalFormId) => FormState;
19
+ registerForm: (formId: InternalFormId) => void;
20
+ cleanupForm: (formId: InternalFormId) => void;
21
+ };
12
22
  export declare type FormState = {
13
23
  isHydrated: boolean;
14
24
  isSubmitting: boolean;
@@ -32,9 +42,6 @@ export declare type FormState = {
32
42
  validate: () => Promise<void>;
33
43
  resetFormElement: () => void;
34
44
  };
35
- export declare const formStore: {
36
- (formId: import("./storeFamily").InternalFormId): import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormState>, "setState"> & {
37
- setState(nextStateOrUpdater: FormState | Partial<FormState> | ((state: import("immer/dist/internal").WritableDraft<FormState>) => void), shouldReplace?: boolean | undefined): void;
38
- }>;
39
- remove(formId: import("./storeFamily").InternalFormId): void;
40
- };
45
+ export declare const useRootFormStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormStoreState>, "setState"> & {
46
+ setState(nextStateOrUpdater: FormStoreState | Partial<FormStoreState> | ((state: WritableDraft<FormStoreState>) => void), shouldReplace?: boolean | undefined): void;
47
+ }>;
@@ -1,5 +1,3 @@
1
- import { ControlledFieldState } from "./controlledFieldStore";
2
1
  import { FormState } from "./createFormStore";
3
- import { InternalFormId } from "./storeFamily";
2
+ import { InternalFormId } from "./types";
4
3
  export declare const useFormStore: <T>(formId: InternalFormId, selector: (state: FormState) => T) => T;
5
- export declare const useControlledFieldStore: <T>(formId: InternalFormId, selector: (state: ControlledFieldState) => T) => T;
@@ -0,0 +1 @@
1
+ export declare type InternalFormId = string | symbol;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix-validated-form",
3
- "version": "4.4.1",
3
+ "version": "4.5.0-beta.0",
4
4
  "description": "Form component and utils for easy form validation in remix",
5
5
  "browser": "./dist/remix-validated-form.cjs.js",
6
6
  "main": "./dist/remix-validated-form.umd.js",
@@ -41,9 +41,9 @@
41
41
  "@remix-run/react": "^1.2.1",
42
42
  "@remix-run/server-runtime": "^1.2.1",
43
43
  "@types/lodash": "^4.14.178",
44
- "@types/react": "^17.0.37",
44
+ "@types/react": "^18.0.9",
45
45
  "fetch-blob": "^3.1.3",
46
- "react": "^17.0.2",
46
+ "react": "^18.1.0",
47
47
  "tsconfig": "*",
48
48
  "typescript": "^4.5.3",
49
49
  "vite-config": "*"
@@ -52,6 +52,6 @@
52
52
  "immer": "^9.0.12",
53
53
  "lodash": "^4.17.21",
54
54
  "tiny-invariant": "^1.2.0",
55
- "zustand": "^4.0.0-rc.0"
55
+ "zustand": "^4.0.0-rc.1"
56
56
  }
57
57
  }
@@ -23,12 +23,12 @@ import {
23
23
  useSetFieldErrors,
24
24
  } from "./internal/hooks";
25
25
  import { MultiValueMap, useMultiValueMap } from "./internal/MultiValueMap";
26
- import { cleanupFormState } from "./internal/state/cleanup";
27
- import { SyncedFormProps } from "./internal/state/createFormStore";
26
+ import { useControlledFieldStore } from "./internal/state/controlledFieldStore";
28
27
  import {
29
- useControlledFieldStore,
30
- useFormStore,
31
- } from "./internal/state/storeHooks";
28
+ SyncedFormProps,
29
+ useRootFormStore,
30
+ } from "./internal/state/createFormStore";
31
+ import { useFormStore } from "./internal/state/storeHooks";
32
32
  import { useSubmitComplete } from "./internal/submissionCallbacks";
33
33
  import {
34
34
  mergeRefs,
@@ -186,6 +186,14 @@ function formEventProxy<T extends object>(event: T): T {
186
186
  }) as T;
187
187
  }
188
188
 
189
+ type HTMLSubmitEvent = React.BaseSyntheticEvent<
190
+ SubmitEvent,
191
+ Event,
192
+ HTMLFormElement
193
+ >;
194
+
195
+ type HTMLFormSubmitter = HTMLButtonElement | HTMLInputElement;
196
+
189
197
  /**
190
198
  * The primary form component of `remix-validated-form`.
191
199
  */
@@ -228,23 +236,16 @@ export function ValidatedForm<DataType>({
228
236
  const setFieldErrors = useSetFieldErrors(formId);
229
237
  const setFieldError = useFormStore(formId, (state) => state.setFieldError);
230
238
  const reset = useFormStore(formId, (state) => state.reset);
231
- const resetControlledFields = useControlledFieldStore(
232
- formId,
233
- (state) => state.reset
234
- );
239
+ const resetControlledFields = useControlledFieldStore((state) => state.reset);
235
240
  const startSubmit = useFormStore(formId, (state) => state.startSubmit);
236
241
  const endSubmit = useFormStore(formId, (state) => state.endSubmit);
237
242
  const syncFormProps = useFormStore(formId, (state) => state.syncFormProps);
238
- const setHydrated = useFormStore(formId, (state) => state.setHydrated);
239
243
  const setFormElementInState = useFormStore(
240
244
  formId,
241
245
  (state) => state.setFormElement
242
246
  );
243
-
244
- useEffect(() => {
245
- setHydrated();
246
- return () => cleanupFormState(formId);
247
- }, [formId, setHydrated]);
247
+ const cleanupForm = useRootFormStore((state) => state.cleanupForm);
248
+ const registerForm = useRootFormStore((state) => state.registerForm);
248
249
 
249
250
  const customFocusHandlers = useMultiValueMap<string, () => void>();
250
251
  const registerReceiveFocus: SyncedFormProps["registerReceiveFocus"] =
@@ -258,6 +259,13 @@ export function ValidatedForm<DataType>({
258
259
  [customFocusHandlers]
259
260
  );
260
261
 
262
+ // TODO: all these hooks running at startup cause extra, unnecessary renders
263
+ // There must be a nice way to avoid this.
264
+ useLayoutEffect(() => {
265
+ registerForm(formId);
266
+ return () => cleanupForm(formId);
267
+ }, [cleanupForm, formId, registerForm]);
268
+
261
269
  useLayoutEffect(() => {
262
270
  syncFormProps({
263
271
  action,
@@ -276,6 +284,10 @@ export function ValidatedForm<DataType>({
276
284
  validator,
277
285
  ]);
278
286
 
287
+ useLayoutEffect(() => {
288
+ setFormElementInState(formRef.current);
289
+ }, [setFormElementInState]);
290
+
279
291
  useEffect(() => {
280
292
  setFieldErrors(backendError?.fieldErrors ?? {});
281
293
  }, [backendError?.fieldErrors, setFieldErrors, setFieldError]);
@@ -331,17 +343,16 @@ export function ValidatedForm<DataType>({
331
343
  return;
332
344
  }
333
345
 
346
+ const submitter = (e as unknown as HTMLSubmitEvent).nativeEvent
347
+ .submitter as HTMLFormSubmitter | null;
348
+
334
349
  // We deviate from the remix code here a bit because of our async submit.
335
350
  // In remix's `FormImpl`, they use `event.currentTarget` to get the form,
336
351
  // but we already have the form in `formRef.current` so we can just use that.
337
352
  // If we use `event.currentTarget` here, it will break because `currentTarget`
338
353
  // will have changed since the start of the submission.
339
- if (fetcher) fetcher.submit(clickedButtonRef.current || formRef.current);
340
- else
341
- submit(clickedButtonRef.current || formRef.current, {
342
- method,
343
- replace,
344
- });
354
+ if (fetcher) fetcher.submit(submitter || e.currentTarget);
355
+ else submit(submitter || e.currentTarget, { method, replace });
345
356
 
346
357
  clickedButtonRef.current = null;
347
358
  }
@@ -349,7 +360,7 @@ export function ValidatedForm<DataType>({
349
360
 
350
361
  return (
351
362
  <Form
352
- ref={mergeRefs([formRef, formRefProp, setFormElementInState])}
363
+ ref={mergeRefs([formRef, formRefProp])}
353
364
  {...rest}
354
365
  id={id}
355
366
  action={action}
@@ -363,7 +374,7 @@ export function ValidatedForm<DataType>({
363
374
  onReset?.(event);
364
375
  if (event.defaultPrevented) return;
365
376
  reset();
366
- resetControlledFields();
377
+ resetControlledFields(formId);
367
378
  }}
368
379
  >
369
380
  <InternalFormContext.Provider value={contextValue}>
@@ -6,8 +6,8 @@ import { FieldErrors, ValidationErrorResponseData } from "..";
6
6
  import { formDefaultValuesKey } from "./constants";
7
7
  import { InternalFormContext, InternalFormContextValue } from "./formContext";
8
8
  import { Hydratable, hydratable } from "./hydratable";
9
- import { InternalFormId } from "./state/storeFamily";
10
9
  import { useFormStore } from "./state/storeHooks";
10
+ import { InternalFormId } from "./state/types";
11
11
 
12
12
  export const useInternalFormContext = (
13
13
  formId?: string | symbol,
@@ -1,91 +1,112 @@
1
- import invariant from "tiny-invariant";
2
1
  import create from "zustand";
3
2
  import { immer } from "zustand/middleware/immer";
4
- import { storeFamily } from "./storeFamily";
3
+ import { InternalFormId } from "./types";
4
+
5
+ export type FieldState = {
6
+ refCount: number;
7
+ value: unknown;
8
+ defaultValue?: unknown;
9
+ hydrated: boolean;
10
+ valueUpdatePromise: Promise<void> | undefined;
11
+ resolveValueUpdate: (() => void) | undefined;
12
+ };
5
13
 
6
14
  export type ControlledFieldState = {
7
- fields: {
8
- [fieldName: string]:
9
- | {
10
- refCount: number;
11
- value: unknown;
12
- defaultValue?: unknown;
13
- hydrated: boolean;
14
- valueUpdatePromise: Promise<void> | undefined;
15
- resolveValueUpdate: (() => void) | undefined;
16
- }
17
- | undefined;
15
+ forms: {
16
+ [formId: InternalFormId]: {
17
+ [fieldName: string]: FieldState | undefined;
18
+ };
18
19
  };
19
- register: (fieldName: string) => void;
20
- unregister: (fieldName: string) => void;
21
- setValue: (fieldName: string, value: unknown) => void;
22
- hydrateWithDefault: (fieldName: string, defaultValue: unknown) => void;
23
- awaitValueUpdate: (fieldName: string) => Promise<void>;
24
- reset: () => void;
20
+ register: (formId: InternalFormId, fieldName: string) => void;
21
+ unregister: (formId: InternalFormId, fieldName: string) => void;
22
+ getField: (
23
+ formId: InternalFormId,
24
+ fieldName: string
25
+ ) => FieldState | undefined;
26
+ setValue: (formId: InternalFormId, fieldName: string, value: unknown) => void;
27
+ hydrateWithDefault: (
28
+ formId: InternalFormId,
29
+ fieldName: string,
30
+ defaultValue: unknown
31
+ ) => void;
32
+ awaitValueUpdate: (
33
+ formId: InternalFormId,
34
+ fieldName: string
35
+ ) => Promise<void>;
36
+ reset: (formId: InternalFormId) => void;
25
37
  };
26
38
 
27
- export const controlledFieldStore = storeFamily(() =>
28
- create<ControlledFieldState>()(
29
- immer((set, get, api) => ({
30
- fields: {},
39
+ export const useControlledFieldStore = create<ControlledFieldState>()(
40
+ immer((set, get) => ({
41
+ forms: {},
42
+
43
+ register: (formId, field) =>
44
+ set((state) => {
45
+ if (!state.forms[formId]) {
46
+ state.forms[formId] = {};
47
+ }
48
+
49
+ if (state.forms[formId][field]) {
50
+ state.forms[formId][field]!.refCount++;
51
+ } else {
52
+ state.forms[formId][field] = {
53
+ refCount: 1,
54
+ value: undefined,
55
+ hydrated: false,
56
+ valueUpdatePromise: undefined,
57
+ resolveValueUpdate: undefined,
58
+ };
59
+ }
60
+ }),
31
61
 
32
- register: (field) =>
33
- set((state) => {
34
- if (state.fields[field]) {
35
- state.fields[field]!.refCount++;
36
- } else {
37
- state.fields[field] = {
38
- refCount: 1,
39
- value: undefined,
40
- hydrated: false,
41
- valueUpdatePromise: undefined,
42
- resolveValueUpdate: undefined,
43
- };
44
- }
45
- }),
62
+ unregister: (formId, field) =>
63
+ set((state) => {
64
+ const formState = state.forms?.[formId];
65
+ const fieldState = formState?.[field];
66
+ if (!fieldState) return;
46
67
 
47
- unregister: (field) =>
48
- set((state) => {
49
- const fieldState = state.fields[field];
50
- if (!fieldState) return;
68
+ fieldState.refCount--;
69
+ if (fieldState.refCount === 0) delete formState[field];
70
+ }),
51
71
 
52
- fieldState.refCount--;
53
- if (fieldState.refCount === 0) delete state.fields[field];
54
- }),
72
+ getField: (formId, field) => {
73
+ return get().forms?.[formId]?.[field];
74
+ },
55
75
 
56
- setValue: (field, value) =>
57
- set((state) => {
58
- const fieldState = state.fields[field];
59
- if (!fieldState) return;
76
+ setValue: (formId, field, value) =>
77
+ set((state) => {
78
+ const fieldState = state.forms?.[formId]?.[field];
79
+ if (!fieldState) return;
60
80
 
61
- fieldState.value = value;
62
- const promise = new Promise<void>((resolve) => {
63
- fieldState.resolveValueUpdate = resolve;
64
- });
65
- fieldState.valueUpdatePromise = promise;
66
- }),
81
+ fieldState.value = value;
82
+ const promise = new Promise<void>((resolve) => {
83
+ fieldState.resolveValueUpdate = resolve;
84
+ });
85
+ fieldState.valueUpdatePromise = promise;
86
+ }),
67
87
 
68
- hydrateWithDefault: (field, defaultValue) =>
69
- set((state) => {
70
- const fieldState = state.fields[field];
71
- if (!fieldState) return;
88
+ hydrateWithDefault: (formId, field, defaultValue) =>
89
+ set((state) => {
90
+ const fieldState = state.forms[formId]?.[field];
91
+ if (!fieldState) return;
72
92
 
73
- fieldState.value = defaultValue;
74
- fieldState.defaultValue = defaultValue;
75
- fieldState.hydrated = true;
76
- }),
93
+ fieldState.value = defaultValue;
94
+ fieldState.defaultValue = defaultValue;
95
+ fieldState.hydrated = true;
96
+ }),
77
97
 
78
- awaitValueUpdate: async (field) => {
79
- await get().fields[field]?.valueUpdatePromise;
80
- },
98
+ awaitValueUpdate: async (formId, field) => {
99
+ await get().getField(formId, field)?.valueUpdatePromise;
100
+ },
81
101
 
82
- reset: () =>
83
- set((state) => {
84
- Object.values(state.fields).forEach((field) => {
85
- if (!field) return;
86
- field.value = field.defaultValue;
87
- });
88
- }),
89
- }))
90
- )
102
+ reset: (formId) =>
103
+ set((state) => {
104
+ const formState = state.forms[formId];
105
+ if (!formState) return;
106
+ Object.values(formState).forEach((field) => {
107
+ if (!field) return;
108
+ field.value = field.defaultValue;
109
+ });
110
+ }),
111
+ }))
91
112
  );
@@ -1,31 +1,37 @@
1
1
  import { useCallback, useEffect } from "react";
2
2
  import { InternalFormContextValue } from "../formContext";
3
3
  import { useFieldDefaultValue } from "../hooks";
4
- import { controlledFieldStore } from "./controlledFieldStore";
5
- import { formStore } from "./createFormStore";
6
- import { InternalFormId } from "./storeFamily";
4
+ import { useControlledFieldStore } from "./controlledFieldStore";
5
+ import { useFormStore } from "./storeHooks";
6
+ import { InternalFormId } from "./types";
7
7
 
8
8
  export const useControlledFieldValue = (
9
9
  context: InternalFormContextValue,
10
10
  field: string
11
11
  ) => {
12
- const useValueStore = controlledFieldStore(context.formId);
13
- const value = useValueStore((state) => state.fields[field]?.value);
12
+ const value = useControlledFieldStore(
13
+ (state) => state.getField(context.formId, field)?.value
14
+ );
14
15
 
15
- const useFormStore = formStore(context.formId);
16
- const isFormHydrated = useFormStore((state) => state.isHydrated);
16
+ const isFormHydrated = useFormStore(
17
+ context.formId,
18
+ (state) => state.isHydrated
19
+ );
17
20
  const defaultValue = useFieldDefaultValue(field, context);
18
21
 
19
- const isFieldHydrated = useValueStore(
20
- (state) => state.fields[field]?.hydrated ?? false
22
+ const isFieldHydrated = useControlledFieldStore(
23
+ (state) => state.getField(context.formId, field)?.hydrated ?? false
24
+ );
25
+ const hydrateWithDefault = useControlledFieldStore(
26
+ (state) => state.hydrateWithDefault
21
27
  );
22
- const hydrateWithDefault = useValueStore((state) => state.hydrateWithDefault);
23
28
 
24
29
  useEffect(() => {
25
30
  if (isFormHydrated && !isFieldHydrated) {
26
- hydrateWithDefault(field, defaultValue);
31
+ hydrateWithDefault(context.formId, field, defaultValue);
27
32
  }
28
33
  }, [
34
+ context.formId,
29
35
  defaultValue,
30
36
  field,
31
37
  hydrateWithDefault,
@@ -40,26 +46,26 @@ export const useControllableValue = (
40
46
  context: InternalFormContextValue,
41
47
  field: string
42
48
  ) => {
43
- const useValueStore = controlledFieldStore(context.formId);
44
-
45
- const resolveUpdate = useValueStore(
46
- (state) => state.fields[field]?.resolveValueUpdate
49
+ const resolveUpdate = useControlledFieldStore(
50
+ (state) => state.getField(context.formId, field)?.resolveValueUpdate
47
51
  );
48
52
  useEffect(() => {
49
53
  resolveUpdate?.();
50
54
  }, [resolveUpdate]);
51
55
 
52
- const register = useValueStore((state) => state.register);
53
- const unregister = useValueStore((state) => state.unregister);
56
+ const register = useControlledFieldStore((state) => state.register);
57
+ const unregister = useControlledFieldStore((state) => state.unregister);
54
58
  useEffect(() => {
55
- register(field);
56
- return () => unregister(field);
59
+ register(context.formId, field);
60
+ return () => unregister(context.formId, field);
57
61
  }, [context.formId, field, register, unregister]);
58
62
 
59
- const setControlledFieldValue = useValueStore((state) => state.setValue);
63
+ const setControlledFieldValue = useControlledFieldStore(
64
+ (state) => state.setValue
65
+ );
60
66
  const setValue = useCallback(
61
- (value: unknown) => setControlledFieldValue(field, value),
62
- [field, setControlledFieldValue]
67
+ (value: unknown) => setControlledFieldValue(context.formId, field, value),
68
+ [context.formId, field, setControlledFieldValue]
63
69
  );
64
70
 
65
71
  const value = useControlledFieldValue(context, field);
@@ -68,11 +74,17 @@ export const useControllableValue = (
68
74
  };
69
75
 
70
76
  export const useUpdateControllableValue = (formId: InternalFormId) => {
71
- const useValueStore = controlledFieldStore(formId);
72
- return useValueStore((state) => state.setValue);
77
+ const setValue = useControlledFieldStore((state) => state.setValue);
78
+ return useCallback(
79
+ (field: string, value: unknown) => setValue(formId, field, value),
80
+ [formId, setValue]
81
+ );
73
82
  };
74
83
 
75
84
  export const useAwaitValue = (formId: InternalFormId) => {
76
- const useValueStore = controlledFieldStore(formId);
77
- return useValueStore((state) => state.awaitValueUpdate);
85
+ const awaitValue = useControlledFieldStore((state) => state.awaitValueUpdate);
86
+ return useCallback(
87
+ (field: string) => awaitValue(formId, field),
88
+ [awaitValue, formId]
89
+ );
78
90
  };