remix-validated-form 4.3.1-beta.0 → 4.4.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 (55) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/browser/ValidatedForm.js +16 -30
  3. package/browser/hooks.d.ts +1 -1
  4. package/browser/hooks.js +10 -9
  5. package/browser/internal/hooks.d.ts +20 -9
  6. package/browser/internal/hooks.js +32 -23
  7. package/browser/internal/logic/getRadioChecked.js +1 -1
  8. package/browser/internal/state/cleanup.d.ts +2 -0
  9. package/browser/internal/state/cleanup.js +6 -0
  10. package/browser/internal/state/controlledFieldStore.d.ts +24 -0
  11. package/browser/internal/state/controlledFieldStore.js +57 -0
  12. package/browser/internal/state/controlledFields.d.ts +3 -116
  13. package/browser/internal/state/controlledFields.js +25 -68
  14. package/browser/internal/state/createFormStore.d.ts +40 -0
  15. package/browser/internal/state/createFormStore.js +83 -0
  16. package/browser/internal/state/storeFamily.d.ts +9 -0
  17. package/browser/internal/state/storeFamily.js +18 -0
  18. package/browser/internal/state/storeHooks.d.ts +5 -0
  19. package/browser/internal/state/storeHooks.js +10 -0
  20. package/browser/unreleased/formStateHooks.d.ts +15 -0
  21. package/browser/unreleased/formStateHooks.js +23 -14
  22. package/browser/userFacingFormContext.d.ts +8 -0
  23. package/browser/userFacingFormContext.js +5 -4
  24. package/dist/remix-validated-form.cjs.js +17 -1
  25. package/dist/remix-validated-form.es.js +1033 -1724
  26. package/dist/remix-validated-form.umd.js +17 -1
  27. package/dist/types/hooks.d.ts +1 -1
  28. package/dist/types/internal/hooks.d.ts +20 -9
  29. package/dist/types/internal/state/cleanup.d.ts +2 -0
  30. package/dist/types/internal/state/controlledFieldStore.d.ts +24 -0
  31. package/dist/types/internal/state/controlledFields.d.ts +3 -116
  32. package/dist/types/internal/state/createFormStore.d.ts +40 -0
  33. package/dist/types/internal/state/storeFamily.d.ts +9 -0
  34. package/dist/types/internal/state/storeHooks.d.ts +5 -0
  35. package/dist/types/unreleased/formStateHooks.d.ts +15 -0
  36. package/dist/types/userFacingFormContext.d.ts +8 -0
  37. package/package.json +4 -3
  38. package/src/ValidatedForm.tsx +25 -47
  39. package/src/hooks.ts +15 -18
  40. package/src/internal/hooks.ts +69 -45
  41. package/src/internal/logic/getRadioChecked.ts +1 -1
  42. package/src/internal/state/cleanup.ts +8 -0
  43. package/src/internal/state/controlledFieldStore.ts +91 -0
  44. package/src/internal/state/controlledFields.ts +31 -123
  45. package/src/internal/state/createFormStore.ts +152 -0
  46. package/src/internal/state/storeFamily.ts +24 -0
  47. package/src/internal/state/storeHooks.ts +22 -0
  48. package/src/unreleased/formStateHooks.ts +50 -27
  49. package/src/userFacingFormContext.ts +17 -5
  50. package/dist/types/internal/reset.d.ts +0 -28
  51. package/dist/types/internal/state/atomUtils.d.ts +0 -38
  52. package/dist/types/internal/state.d.ts +0 -343
  53. package/src/internal/reset.ts +0 -26
  54. package/src/internal/state/atomUtils.ts +0 -13
  55. package/src/internal/state.ts +0 -124
@@ -1,93 +1,50 @@
1
- import { atom } from "jotai";
2
- import { useAtomCallback } from "jotai/utils";
3
- import omit from "lodash/omit";
4
1
  import { useCallback, useEffect } from "react";
5
- import { useFieldDefaultValue, useFormAtomValue, useFormAtom, useFormUpdateAtom, } from "../hooks";
6
- import { isHydratedAtom } from "../state";
7
- import { fieldAtomFamily, formAtomFamily, } from "./atomUtils";
8
- export const controlledFieldsAtom = formAtomFamily({});
9
- const refCountAtom = fieldAtomFamily(() => atom(0));
10
- const fieldValueAtom = fieldAtomFamily(() => atom(undefined));
11
- const fieldValueHydratedAtom = fieldAtomFamily(() => atom(false));
12
- export const valueUpdatePromiseAtom = fieldAtomFamily(() => atom(undefined));
13
- export const resolveValueUpdateAtom = fieldAtomFamily(() => atom(undefined));
14
- const registerAtom = atom(null, (get, set, { formId, field }) => {
15
- set(refCountAtom({ formId, field }), (prev) => prev + 1);
16
- const newRefCount = get(refCountAtom({ formId, field }));
17
- // We don't set hydrated here because it gets set when we know
18
- // we have the right default values
19
- if (newRefCount === 1) {
20
- set(controlledFieldsAtom(formId), (prev) => ({
21
- ...prev,
22
- [field]: fieldValueAtom({ formId, field }),
23
- }));
24
- }
25
- });
26
- const unregisterAtom = atom(null, (get, set, { formId, field }) => {
27
- set(refCountAtom({ formId, field }), (prev) => prev - 1);
28
- const newRefCount = get(refCountAtom({ formId, field }));
29
- if (newRefCount === 0) {
30
- set(controlledFieldsAtom(formId), (prev) => omit(prev, field));
31
- fieldValueAtom.remove({ formId, field });
32
- resolveValueUpdateAtom.remove({ formId, field });
33
- fieldValueHydratedAtom.remove({ formId, field });
34
- }
35
- });
36
- export const setControlledFieldValueAtom = atom(null, (_get, set, { formId, field, value, }) => {
37
- set(fieldValueAtom({ formId, field }), value);
38
- const resolveAtom = resolveValueUpdateAtom({ formId, field });
39
- const promiseAtom = valueUpdatePromiseAtom({ formId, field });
40
- const promise = new Promise((resolve) => set(resolveAtom, () => {
41
- resolve();
42
- set(resolveAtom, undefined);
43
- set(promiseAtom, undefined);
44
- }));
45
- set(promiseAtom, promise);
46
- });
2
+ import { useFieldDefaultValue } from "../hooks";
3
+ import { controlledFieldStore } from "./controlledFieldStore";
4
+ import { formStore } from "./createFormStore";
47
5
  export const useControlledFieldValue = (context, field) => {
48
- const fieldAtom = fieldValueAtom({ formId: context.formId, field });
49
- const [value, setValue] = useFormAtom(fieldAtom);
6
+ const useValueStore = controlledFieldStore(context.formId);
7
+ const value = useValueStore((state) => { var _a; return (_a = state.fields[field]) === null || _a === void 0 ? void 0 : _a.value; });
8
+ const useFormStore = formStore(context.formId);
9
+ const isFormHydrated = useFormStore((state) => state.isHydrated);
50
10
  const defaultValue = useFieldDefaultValue(field, context);
51
- const isHydrated = useFormAtomValue(isHydratedAtom(context.formId));
52
- const [isFieldHydrated, setIsFieldHydrated] = useFormAtom(fieldValueHydratedAtom({ formId: context.formId, field }));
11
+ const isFieldHydrated = useValueStore((state) => { var _a, _b; return (_b = (_a = state.fields[field]) === null || _a === void 0 ? void 0 : _a.hydrated) !== null && _b !== void 0 ? _b : false; });
12
+ const hydrateWithDefault = useValueStore((state) => state.hydrateWithDefault);
53
13
  useEffect(() => {
54
- if (isHydrated && !isFieldHydrated) {
55
- setValue(defaultValue);
56
- setIsFieldHydrated(true);
14
+ if (isFormHydrated && !isFieldHydrated) {
15
+ hydrateWithDefault(field, defaultValue);
57
16
  }
58
17
  }, [
59
18
  defaultValue,
60
19
  field,
61
- context.formId,
20
+ hydrateWithDefault,
62
21
  isFieldHydrated,
63
- isHydrated,
64
- setIsFieldHydrated,
65
- setValue,
22
+ isFormHydrated,
66
23
  ]);
67
24
  return isFieldHydrated ? value : defaultValue;
68
25
  };
69
26
  export const useControllableValue = (context, field) => {
70
- const resolveUpdate = useFormAtomValue(resolveValueUpdateAtom({ formId: context.formId, field }));
27
+ const useValueStore = controlledFieldStore(context.formId);
28
+ const resolveUpdate = useValueStore((state) => { var _a; return (_a = state.fields[field]) === null || _a === void 0 ? void 0 : _a.resolveValueUpdate; });
71
29
  useEffect(() => {
72
30
  resolveUpdate === null || resolveUpdate === void 0 ? void 0 : resolveUpdate();
73
31
  }, [resolveUpdate]);
74
- const register = useFormUpdateAtom(registerAtom);
75
- const unregister = useFormUpdateAtom(unregisterAtom);
32
+ const register = useValueStore((state) => state.register);
33
+ const unregister = useValueStore((state) => state.unregister);
76
34
  useEffect(() => {
77
- register({ formId: context.formId, field });
78
- return () => unregister({ formId: context.formId, field });
35
+ register(field);
36
+ return () => unregister(field);
79
37
  }, [context.formId, field, register, unregister]);
80
- const setControlledFieldValue = useFormUpdateAtom(setControlledFieldValueAtom);
81
- const setValue = useCallback((value) => setControlledFieldValue({ formId: context.formId, field, value }), [field, context.formId, setControlledFieldValue]);
38
+ const setControlledFieldValue = useValueStore((state) => state.setValue);
39
+ const setValue = useCallback((value) => setControlledFieldValue(field, value), [field, setControlledFieldValue]);
82
40
  const value = useControlledFieldValue(context, field);
83
41
  return [value, setValue];
84
42
  };
85
43
  export const useUpdateControllableValue = (formId) => {
86
- const setControlledFieldValue = useFormUpdateAtom(setControlledFieldValueAtom);
87
- return useCallback((field, value) => setControlledFieldValue({ formId, field, value }), [formId, setControlledFieldValue]);
44
+ const useValueStore = controlledFieldStore(formId);
45
+ return useValueStore((state) => state.setValue);
88
46
  };
89
47
  export const useAwaitValue = (formId) => {
90
- return useAtomCallback(useCallback(async (get, _set, field) => {
91
- await get(valueUpdatePromiseAtom({ formId, field }));
92
- }, [formId]));
48
+ const useValueStore = controlledFieldStore(formId);
49
+ return useValueStore((state) => state.awaitValueUpdate);
93
50
  };
@@ -0,0 +1,40 @@
1
+ import { FieldErrors, TouchedFields, Validator } from "../../validation/types";
2
+ export declare type SyncedFormProps = {
3
+ formId?: string;
4
+ action?: string;
5
+ subaction?: string;
6
+ defaultValues: {
7
+ [fieldName: string]: any;
8
+ };
9
+ registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
10
+ validator: Validator<unknown>;
11
+ };
12
+ export declare type FormState = {
13
+ isHydrated: boolean;
14
+ isSubmitting: boolean;
15
+ hasBeenSubmitted: boolean;
16
+ fieldErrors: FieldErrors;
17
+ touchedFields: TouchedFields;
18
+ formProps?: SyncedFormProps;
19
+ formElement: HTMLFormElement | null;
20
+ isValid: () => boolean;
21
+ startSubmit: () => void;
22
+ endSubmit: () => void;
23
+ setTouched: (field: string, touched: boolean) => void;
24
+ setFieldError: (field: string, error: string) => void;
25
+ setFieldErrors: (errors: FieldErrors) => void;
26
+ clearFieldError: (field: string) => void;
27
+ reset: () => void;
28
+ syncFormProps: (props: SyncedFormProps) => void;
29
+ setHydrated: () => void;
30
+ setFormElement: (formElement: HTMLFormElement | null) => void;
31
+ validateField: (fieldName: string) => Promise<string | null>;
32
+ validate: () => Promise<void>;
33
+ resetFormElement: () => void;
34
+ };
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
+ };
@@ -0,0 +1,83 @@
1
+ import invariant from "tiny-invariant";
2
+ import create from "zustand";
3
+ import { immer } from "zustand/middleware/immer";
4
+ import { controlledFieldStore } from "./controlledFieldStore";
5
+ import { storeFamily } from "./storeFamily";
6
+ export const formStore = storeFamily((formId) => create()(immer((set, get, api) => ({
7
+ isHydrated: false,
8
+ isSubmitting: false,
9
+ hasBeenSubmitted: false,
10
+ touchedFields: {},
11
+ fieldErrors: {},
12
+ formElement: null,
13
+ isValid: () => Object.keys(get().fieldErrors).length === 0,
14
+ startSubmit: () => set((state) => {
15
+ state.isSubmitting = true;
16
+ state.hasBeenSubmitted = true;
17
+ }),
18
+ endSubmit: () => set((state) => {
19
+ state.isSubmitting = false;
20
+ }),
21
+ setTouched: (fieldName, touched) => set((state) => {
22
+ state.touchedFields[fieldName] = touched;
23
+ }),
24
+ setFieldError: (fieldName, error) => set((state) => {
25
+ state.fieldErrors[fieldName] = error;
26
+ }),
27
+ setFieldErrors: (errors) => set((state) => {
28
+ state.fieldErrors = errors;
29
+ }),
30
+ clearFieldError: (fieldName) => set((state) => {
31
+ delete state.fieldErrors[fieldName];
32
+ }),
33
+ reset: () => set((state) => {
34
+ state.fieldErrors = {};
35
+ state.touchedFields = {};
36
+ state.hasBeenSubmitted = false;
37
+ }),
38
+ syncFormProps: (props) => set((state) => {
39
+ state.formProps = props;
40
+ }),
41
+ setHydrated: () => set((state) => {
42
+ state.isHydrated = true;
43
+ }),
44
+ setFormElement: (formElement) => {
45
+ // This gets called frequently, so we want to avoid calling set() every time
46
+ // Or else we wind up with an infinite loop
47
+ if (get().formElement === formElement)
48
+ return;
49
+ set((state) => {
50
+ // weird type issue here
51
+ // seems to be because formElement is a writable draft
52
+ state.formElement = formElement;
53
+ });
54
+ },
55
+ validateField: async (field) => {
56
+ var _a, _b, _c;
57
+ const formElement = get().formElement;
58
+ invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
59
+ const validator = (_a = get().formProps) === null || _a === void 0 ? void 0 : _a.validator;
60
+ invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
61
+ await ((_c = (_b = controlledFieldStore(formId).getState()).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, field));
62
+ const { error } = await validator.validateField(new FormData(formElement), field);
63
+ if (error) {
64
+ get().setFieldError(field, error);
65
+ return error;
66
+ }
67
+ else {
68
+ get().clearFieldError(field);
69
+ return null;
70
+ }
71
+ },
72
+ validate: async () => {
73
+ var _a;
74
+ const formElement = get().formElement;
75
+ invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
76
+ const validator = (_a = get().formProps) === null || _a === void 0 ? void 0 : _a.validator;
77
+ invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
78
+ const { error } = await validator.validate(new FormData(formElement));
79
+ if (error)
80
+ get().setFieldErrors(error.fieldErrors);
81
+ },
82
+ resetFormElement: () => { var _a; return (_a = get().formElement) === null || _a === void 0 ? void 0 : _a.reset(); },
83
+ }))));
@@ -0,0 +1,9 @@
1
+ /**
2
+ * This is basically what `atomFamily` from jotai does,
3
+ * but it doesn't make sense to include the entire jotai library just for that api.
4
+ */
5
+ export declare type InternalFormId = string | symbol;
6
+ export declare const storeFamily: <T>(create: (formId: InternalFormId) => T) => {
7
+ (formId: InternalFormId): T;
8
+ remove(formId: InternalFormId): void;
9
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * This is basically what `atomFamily` from jotai does,
3
+ * but it doesn't make sense to include the entire jotai library just for that api.
4
+ */
5
+ export const storeFamily = (create) => {
6
+ const stores = new Map();
7
+ const family = (formId) => {
8
+ if (stores.has(formId))
9
+ return stores.get(formId);
10
+ const store = create(formId);
11
+ stores.set(formId, store);
12
+ return store;
13
+ };
14
+ family.remove = (formId) => {
15
+ stores.delete(formId);
16
+ };
17
+ return family;
18
+ };
@@ -0,0 +1,5 @@
1
+ import { ControlledFieldState } from "./controlledFieldStore";
2
+ import { FormState } from "./createFormStore";
3
+ import { InternalFormId } from "./storeFamily";
4
+ 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,10 @@
1
+ import { controlledFieldStore, } from "./controlledFieldStore";
2
+ import { formStore } from "./createFormStore";
3
+ export const useFormStore = (formId, selector) => {
4
+ const useStore = formStore(formId);
5
+ return useStore(selector);
6
+ };
7
+ export const useControlledFieldStore = (formId, selector) => {
8
+ const useStore = controlledFieldStore(formId);
9
+ return useStore(selector);
10
+ };
@@ -30,6 +30,21 @@ export declare type FormHelpers = {
30
30
  * Change the touched state of the specified field.
31
31
  */
32
32
  setTouched: (fieldName: string, touched: boolean) => void;
33
+ /**
34
+ * Validate the whole form and populate any errors.
35
+ */
36
+ validate: () => Promise<void>;
37
+ /**
38
+ * Clears all errors on the form.
39
+ */
40
+ clearAllErrors: () => void;
41
+ /**
42
+ * Resets the form.
43
+ *
44
+ * _Note_: The equivalent behavior can be achieved by calling formElement.reset()
45
+ * or clicking a button element with `type="reset"`.
46
+ */
47
+ reset: () => void;
33
48
  };
34
49
  /**
35
50
  * Returns helpers that can be used to update the form state.
@@ -1,25 +1,27 @@
1
1
  import { useMemo } from "react";
2
- import { useInternalFormContext, useClearError, useSetTouched, useDefaultValuesForForm, useFieldErrorsForForm, useFormAtomValue, } from "../internal/hooks";
3
- import { fieldErrorsAtom, formPropsAtom, hasBeenSubmittedAtom, isSubmittingAtom, isValidAtom, touchedFieldsAtom, } from "../internal/state";
2
+ import { useInternalFormContext, useClearError, useSetTouched, useDefaultValuesForForm, useFieldErrorsForForm, useInternalIsSubmitting, useInternalHasBeenSubmitted, useTouchedFields, useInternalIsValid, useFieldErrors, useValidateField, useValidate, useSetFieldErrors, useResetFormElement, useSyncedDefaultValues, useFormActionProp, useFormSubactionProp, } from "../internal/hooks";
4
3
  /**
5
4
  * Returns information about the form.
6
5
  *
7
6
  * @param formId the id of the form. Only necessary if being used outside a ValidatedForm.
8
7
  */
9
8
  export const useFormState = (formId) => {
10
- const formContext = useInternalFormContext(formId, "useIsValid");
11
- const formProps = useFormAtomValue(formPropsAtom(formContext.formId));
12
- const isSubmitting = useFormAtomValue(isSubmittingAtom(formContext.formId));
13
- const hasBeenSubmitted = useFormAtomValue(hasBeenSubmittedAtom(formContext.formId));
14
- const touchedFields = useFormAtomValue(touchedFieldsAtom(formContext.formId));
15
- const isValid = useFormAtomValue(isValidAtom(formContext.formId));
9
+ const formContext = useInternalFormContext(formId, "useFormState");
10
+ const isSubmitting = useInternalIsSubmitting(formContext.formId);
11
+ const hasBeenSubmitted = useInternalHasBeenSubmitted(formContext.formId);
12
+ const touchedFields = useTouchedFields(formContext.formId);
13
+ const isValid = useInternalIsValid(formContext.formId);
14
+ const action = useFormActionProp(formContext.formId);
15
+ const subaction = useFormSubactionProp(formContext.formId);
16
+ const syncedDefaultValues = useSyncedDefaultValues(formContext.formId);
16
17
  const defaultValuesToUse = useDefaultValuesForForm(formContext);
17
- const hydratedDefaultValues = defaultValuesToUse.hydrateTo(formProps.defaultValues);
18
- const fieldErrorsFromState = useFormAtomValue(fieldErrorsAtom(formContext.formId));
18
+ const hydratedDefaultValues = defaultValuesToUse.hydrateTo(syncedDefaultValues);
19
+ const fieldErrorsFromState = useFieldErrors(formContext.formId);
19
20
  const fieldErrorsToUse = useFieldErrorsForForm(formContext);
20
21
  const hydratedFieldErrors = fieldErrorsToUse.hydrateTo(fieldErrorsFromState);
21
22
  return useMemo(() => ({
22
- ...formProps,
23
+ action,
24
+ subaction,
23
25
  defaultValues: hydratedDefaultValues,
24
26
  fieldErrors: hydratedFieldErrors !== null && hydratedFieldErrors !== void 0 ? hydratedFieldErrors : {},
25
27
  hasBeenSubmitted,
@@ -27,12 +29,13 @@ export const useFormState = (formId) => {
27
29
  touchedFields,
28
30
  isValid,
29
31
  }), [
30
- formProps,
32
+ action,
31
33
  hasBeenSubmitted,
32
34
  hydratedDefaultValues,
33
35
  hydratedFieldErrors,
34
36
  isSubmitting,
35
37
  isValid,
38
+ subaction,
36
39
  touchedFields,
37
40
  ]);
38
41
  };
@@ -44,11 +47,17 @@ export const useFormState = (formId) => {
44
47
  export const useFormHelpers = (formId) => {
45
48
  const formContext = useInternalFormContext(formId, "useFormHelpers");
46
49
  const setTouched = useSetTouched(formContext);
47
- const { validateField } = useFormAtomValue(formPropsAtom(formContext.formId));
50
+ const validateField = useValidateField(formContext.formId);
51
+ const validate = useValidate(formContext.formId);
48
52
  const clearError = useClearError(formContext);
53
+ const setFieldErrors = useSetFieldErrors(formContext.formId);
54
+ const reset = useResetFormElement(formContext.formId);
49
55
  return useMemo(() => ({
50
56
  setTouched,
51
57
  validateField,
52
58
  clearError,
53
- }), [clearError, setTouched, validateField]);
59
+ validate,
60
+ clearAllErrors: () => setFieldErrors({}),
61
+ reset,
62
+ }), [clearError, reset, setFieldErrors, setTouched, validate, validateField]);
54
63
  };
@@ -53,6 +53,14 @@ export declare type FormContextValue = {
53
53
  * Change the touched state of the specified field.
54
54
  */
55
55
  setFieldTouched: (fieldName: string, touched: boolean) => void;
56
+ /**
57
+ * Validate the whole form and populate any errors.
58
+ */
59
+ validate: () => Promise<void>;
60
+ /**
61
+ * Clears all errors on the form.
62
+ */
63
+ clearAllErrors: () => void;
56
64
  };
57
65
  /**
58
66
  * Provides access to some of the internal state of the form.
@@ -1,6 +1,5 @@
1
1
  import { useCallback } from "react";
2
- import { useFormAtomValue, useInternalFormContext } from "./internal/hooks";
3
- import { formPropsAtom } from "./internal/state";
2
+ import { useInternalFormContext, useRegisterReceiveFocus, } from "./internal/hooks";
4
3
  import { useFormHelpers, useFormState } from "./unreleased/formStateHooks";
5
4
  /**
6
5
  * Provides access to some of the internal state of the form.
@@ -9,8 +8,8 @@ export const useFormContext = (formId) => {
9
8
  // Try to access context so we get our error specific to this hook if it's not there
10
9
  const context = useInternalFormContext(formId, "useFormContext");
11
10
  const state = useFormState(formId);
12
- const { clearError: internalClearError, setTouched, validateField, } = useFormHelpers(formId);
13
- const { registerReceiveFocus } = useFormAtomValue(formPropsAtom(context.formId));
11
+ const { clearError: internalClearError, setTouched, validateField, clearAllErrors, validate, } = useFormHelpers(formId);
12
+ const registerReceiveFocus = useRegisterReceiveFocus(context.formId);
14
13
  const clearError = useCallback((...names) => {
15
14
  names.forEach((name) => {
16
15
  internalClearError(name);
@@ -22,5 +21,7 @@ export const useFormContext = (formId) => {
22
21
  validateField,
23
22
  clearError,
24
23
  registerReceiveFocus,
24
+ clearAllErrors,
25
+ validate,
25
26
  };
26
27
  };