@tanstack/react-form 1.27.7 → 1.28.1

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.
@@ -7,6 +7,15 @@ const useForm = require("./useForm.cjs");
7
7
  const useFieldGroup = require("./useFieldGroup.cjs");
8
8
  const fieldContext = React.createContext(null);
9
9
  const formContext = React.createContext(null);
10
+ function useFormContext() {
11
+ const form = React.useContext(formContext);
12
+ if (!form) {
13
+ throw new Error(
14
+ "`formContext` only works when within a `formComponent` passed to `createFormHook`"
15
+ );
16
+ }
17
+ return form;
18
+ }
10
19
  function createFormHookContexts() {
11
20
  function useFieldContext() {
12
21
  const field = React.useContext(fieldContext);
@@ -17,15 +26,6 @@ function createFormHookContexts() {
17
26
  }
18
27
  return field;
19
28
  }
20
- function useFormContext() {
21
- const form = React.useContext(formContext);
22
- if (!form) {
23
- throw new Error(
24
- "`formContext` only works when within a `formComponent` passed to `createFormHook`"
25
- );
26
- }
27
- return form;
28
- }
29
29
  return { fieldContext, useFieldContext, useFormContext, formContext };
30
30
  }
31
31
  function createFormHook({
@@ -83,10 +83,15 @@ function createFormHook({
83
83
  return render({ ...props, ...innerProps, group: fieldGroupApi });
84
84
  };
85
85
  }
86
+ function useTypedAppFormContext(_props) {
87
+ const form = useFormContext();
88
+ return form;
89
+ }
86
90
  return {
87
91
  useAppForm,
88
92
  withForm,
89
- withFieldGroup
93
+ withFieldGroup,
94
+ useTypedAppFormContext
90
95
  };
91
96
  }
92
97
  exports.createFormHook = createFormHook;
@@ -1 +1 @@
1
- {"version":3,"file":"createFormHook.cjs","sources":["../../src/createFormHook.tsx"],"sourcesContent":["'use client'\n/* eslint-disable @eslint-react/no-context-provider */\nimport { createContext, useContext, useMemo } from 'react'\nimport { useForm } from './useForm'\nimport { useFieldGroup } from './useFieldGroup'\nimport type {\n AnyFieldApi,\n AnyFormApi,\n BaseFormOptions,\n DeepKeysOfType,\n FieldApi,\n FieldsMap,\n FormAsyncValidateOrFn,\n FormOptions,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type {\n ComponentType,\n Context,\n FunctionComponent,\n PropsWithChildren,\n} from 'react'\nimport type { FieldComponent } from './useField'\nimport type { ReactFormExtendedApi } from './useForm'\nimport type { AppFieldExtendedReactFieldGroupApi } from './useFieldGroup'\n\n// We should never hit the `null` case here\nconst fieldContext = createContext<AnyFieldApi>(null as never)\nconst formContext = createContext<AnyFormApi>(null as never)\n\n/**\n * TypeScript inferencing is weird.\n *\n * If you have:\n *\n * @example\n *\n * interface Args<T> {\n * arg?: T\n * }\n *\n * function test<T>(arg?: Partial<Args<T>>): T {\n * return 0 as any;\n * }\n *\n * const a = test({});\n *\n * Then `T` will default to `unknown`.\n *\n * However, if we change `test` to be:\n *\n * @example\n *\n * function test<T extends undefined>(arg?: Partial<Args<T>>): T;\n *\n * Then `T` becomes `undefined`.\n *\n * Here, we are checking if the passed type `T` extends `DefaultT` and **only**\n * `DefaultT`, as if that's the case we assume that inferencing has not occurred.\n */\ntype UnwrapOrAny<T> = [unknown] extends [T] ? any : T\ntype UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T]\n ? [T] extends [DefaultT]\n ? any\n : T\n : T\n\nexport function createFormHookContexts() {\n function useFieldContext<TData>() {\n const field = useContext(fieldContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!field) {\n throw new Error(\n '`fieldContext` only works when within a `fieldComponent` passed to `createFormHook`',\n )\n }\n\n return field as FieldApi<\n any,\n string,\n TData,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n }\n\n function useFormContext() {\n const form = useContext(formContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!form) {\n throw new Error(\n '`formContext` only works when within a `formComponent` passed to `createFormHook`',\n )\n }\n\n return form as ReactFormExtendedApi<\n // If you need access to the form data, you need to use `withForm` instead\n Record<string, never>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n }\n\n return { fieldContext, useFieldContext, useFormContext, formContext }\n}\n\ninterface CreateFormHookProps<\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> {\n fieldComponents: TFieldComponents\n fieldContext: Context<AnyFieldApi>\n formComponents: TFormComponents\n formContext: Context<AnyFormApi>\n}\n\n/**\n * @private\n */\nexport type AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> = ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n NoInfer<TFormComponents> & {\n AppField: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n NoInfer<TFieldComponents>\n >\n AppForm: ComponentType<\n // PropsWithChildren<P> is not optional in React 17\n PropsWithChildren<{}>\n >\n }\n\nexport interface WithFormProps<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TRenderProps extends object = Record<string, never>,\n> extends FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n form: AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport interface WithFieldGroupProps<\n TFieldGroupData,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TSubmitMeta,\n TRenderProps extends object = Record<string, never>,\n> extends BaseFormOptions<TFieldGroupData, TSubmitMeta> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n group: AppFieldExtendedReactFieldGroupApi<\n unknown,\n TFieldGroupData,\n string | FieldsMap<unknown, TFieldGroupData>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n // this types it as 'never' in the render prop. It should prevent any\n // untyped meta passed to the handleSubmit by accident.\n unknown extends TSubmitMeta ? never : TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport function createFormHook<\n const TComponents extends Record<string, ComponentType<any>>,\n const TFormComponents extends Record<string, ComponentType<any>>,\n>({\n fieldComponents,\n fieldContext,\n formContext,\n formComponents,\n}: CreateFormHookProps<TComponents, TFormComponents>) {\n function useAppForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n >(\n props: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ): AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents\n > {\n const form = useForm(props)\n\n // PropsWithChildren<P> is not optional in React 17\n const AppForm = useMemo<ComponentType<PropsWithChildren<{}>>>(() => {\n return ({ children }) => {\n return (\n <formContext.Provider value={form}>{children}</formContext.Provider>\n )\n }\n }, [form])\n\n const AppField = useMemo(() => {\n const AppField = (({ children, ...props }) => {\n return (\n <form.Field {...props}>\n {(field) => (\n // eslint-disable-next-line @eslint-react/no-context-provider\n <fieldContext.Provider value={field}>\n {children(Object.assign(field, fieldComponents))}\n </fieldContext.Provider>\n )}\n </form.Field>\n )\n }) as FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents\n >\n return AppField\n }, [form])\n\n const extendedForm = useMemo(() => {\n return Object.assign(form, {\n AppField,\n AppForm,\n ...formComponents,\n })\n }, [form, AppField, AppForm])\n\n return extendedForm\n }\n\n function withForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n }: WithFormProps<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents,\n TRenderProps\n >): WithFormProps<\n UnwrapOrAny<TFormData>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnDynamic>,\n UnwrapDefaultOrAny<\n undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync\n >,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>,\n UnwrapOrAny<TSubmitMeta>,\n UnwrapOrAny<TComponents>,\n UnwrapOrAny<TFormComponents>,\n UnwrapOrAny<TRenderProps>\n >['render'] {\n return (innerProps) => render({ ...props, ...innerProps })\n }\n\n function withFieldGroup<\n TFieldGroupData,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n defaultValues,\n }: WithFieldGroupProps<\n TFieldGroupData,\n TComponents,\n TFormComponents,\n TSubmitMeta,\n TRenderProps\n >): <\n TFormData,\n TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TFormSubmitMeta,\n >(\n params: PropsWithChildren<\n NoInfer<TRenderProps> & {\n form:\n | AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n | AppFieldExtendedReactFieldGroupApi<\n // Since this only occurs if you nest it within other field groups, it can be more\n // lenient with the types.\n unknown,\n TFormData,\n string | FieldsMap<unknown, TFormData>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n fields: TFields\n }\n >,\n ) => ReturnType<FunctionComponent> {\n return function Render(innerProps) {\n const fieldGroupProps = useMemo(() => {\n return {\n form: innerProps.form,\n fields: innerProps.fields,\n defaultValues,\n formComponents,\n }\n }, [innerProps.form, innerProps.fields])\n const fieldGroupApi = useFieldGroup(fieldGroupProps as any)\n\n return render({ ...props, ...innerProps, group: fieldGroupApi as any })\n }\n }\n\n return {\n useAppForm,\n withForm,\n withFieldGroup,\n }\n}\n"],"names":["fieldContext","formContext","AppField"],"mappings":";;;;;;;AA2BA;AACA;AAuCO;AACL;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAAO;AA2BT;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAAO;AAiBT;AACF;AA+JO;AAGL;AACA;AACAA;AACAC;AAEF;AACE;AA4CE;AAGA;AACE;AACE;AAC+C;AAEjD;AAGF;AACE;AACE;AAEM;AAAA;AAIA;AAEJ;AAiBJ;AAAOC;AAGT;AACE;AAA2B;AACzB;AACA;AACG;AACJ;AAGH;AAAO;AAGT;AAcE;AACA;AACA;AAqCA;AAAyD;AAG3D;AAIE;AACA;AACA;AACA;AAmEA;AACE;AACE;AAAO;AACY;AACE;AACnB;AACA;AAAA;AAGJ;AAEA;AAAsE;AACxE;AAGF;AAAO;AACL;AACA;AACA;AAEJ;;;"}
1
+ {"version":3,"file":"createFormHook.cjs","sources":["../../src/createFormHook.tsx"],"sourcesContent":["'use client'\n/* eslint-disable @eslint-react/no-context-provider */\nimport { createContext, useContext, useMemo } from 'react'\nimport { useForm } from './useForm'\nimport { useFieldGroup } from './useFieldGroup'\nimport type {\n AnyFieldApi,\n AnyFormApi,\n BaseFormOptions,\n DeepKeysOfType,\n FieldApi,\n FieldsMap,\n FormAsyncValidateOrFn,\n FormOptions,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type {\n ComponentType,\n Context,\n FunctionComponent,\n PropsWithChildren,\n} from 'react'\nimport type { FieldComponent } from './useField'\nimport type { ReactFormExtendedApi } from './useForm'\nimport type { AppFieldExtendedReactFieldGroupApi } from './useFieldGroup'\n\n// We should never hit the `null` case here\nconst fieldContext = createContext<AnyFieldApi>(null as never)\nconst formContext = createContext<AnyFormApi>(null as never)\n\n/**\n * TypeScript inferencing is weird.\n *\n * If you have:\n *\n * @example\n *\n * interface Args<T> {\n * arg?: T\n * }\n *\n * function test<T>(arg?: Partial<Args<T>>): T {\n * return 0 as any;\n * }\n *\n * const a = test({});\n *\n * Then `T` will default to `unknown`.\n *\n * However, if we change `test` to be:\n *\n * @example\n *\n * function test<T extends undefined>(arg?: Partial<Args<T>>): T;\n *\n * Then `T` becomes `undefined`.\n *\n * Here, we are checking if the passed type `T` extends `DefaultT` and **only**\n * `DefaultT`, as if that's the case we assume that inferencing has not occurred.\n */\ntype UnwrapOrAny<T> = [unknown] extends [T] ? any : T\ntype UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T]\n ? [T] extends [DefaultT]\n ? any\n : T\n : T\n\nfunction useFormContext() {\n const form = useContext(formContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!form) {\n throw new Error(\n '`formContext` only works when within a `formComponent` passed to `createFormHook`',\n )\n }\n\n return form as ReactFormExtendedApi<\n // If you need access to the form data, you need to use `withForm` instead\n Record<string, never>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n}\n\nexport function createFormHookContexts() {\n function useFieldContext<TData>() {\n const field = useContext(fieldContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!field) {\n throw new Error(\n '`fieldContext` only works when within a `fieldComponent` passed to `createFormHook`',\n )\n }\n\n return field as FieldApi<\n any,\n string,\n TData,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n }\n\n return { fieldContext, useFieldContext, useFormContext, formContext }\n}\n\ninterface CreateFormHookProps<\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> {\n fieldComponents: TFieldComponents\n fieldContext: Context<AnyFieldApi>\n formComponents: TFormComponents\n formContext: Context<AnyFormApi>\n}\n\n/**\n * @private\n */\nexport type AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> = ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n NoInfer<TFormComponents> & {\n AppField: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n NoInfer<TFieldComponents>\n >\n AppForm: ComponentType<\n // PropsWithChildren<P> is not optional in React 17\n PropsWithChildren<{}>\n >\n }\n\nexport interface WithFormProps<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TRenderProps extends object = Record<string, never>,\n> extends FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n form: AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport interface WithFieldGroupProps<\n TFieldGroupData,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TSubmitMeta,\n TRenderProps extends object = Record<string, never>,\n> extends BaseFormOptions<TFieldGroupData, TSubmitMeta> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n group: AppFieldExtendedReactFieldGroupApi<\n unknown,\n TFieldGroupData,\n string | FieldsMap<unknown, TFieldGroupData>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n // this types it as 'never' in the render prop. It should prevent any\n // untyped meta passed to the handleSubmit by accident.\n unknown extends TSubmitMeta ? never : TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport function createFormHook<\n const TComponents extends Record<string, ComponentType<any>>,\n const TFormComponents extends Record<string, ComponentType<any>>,\n>({\n fieldComponents,\n fieldContext,\n formContext,\n formComponents,\n}: CreateFormHookProps<TComponents, TFormComponents>) {\n function useAppForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n >(\n props: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ): AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents\n > {\n const form = useForm(props)\n\n // PropsWithChildren<P> is not optional in React 17\n const AppForm = useMemo<ComponentType<PropsWithChildren<{}>>>(() => {\n return ({ children }) => {\n return (\n <formContext.Provider value={form}>{children}</formContext.Provider>\n )\n }\n }, [form])\n\n const AppField = useMemo(() => {\n const AppField = (({ children, ...props }) => {\n return (\n <form.Field {...props}>\n {(field) => (\n // eslint-disable-next-line @eslint-react/no-context-provider\n <fieldContext.Provider value={field}>\n {children(Object.assign(field, fieldComponents))}\n </fieldContext.Provider>\n )}\n </form.Field>\n )\n }) as FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents\n >\n return AppField\n }, [form])\n\n const extendedForm = useMemo(() => {\n return Object.assign(form, {\n AppField,\n AppForm,\n ...formComponents,\n })\n }, [form, AppField, AppForm])\n\n return extendedForm\n }\n\n function withForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n }: WithFormProps<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents,\n TRenderProps\n >): WithFormProps<\n UnwrapOrAny<TFormData>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnDynamic>,\n UnwrapDefaultOrAny<\n undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync\n >,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>,\n UnwrapOrAny<TSubmitMeta>,\n UnwrapOrAny<TComponents>,\n UnwrapOrAny<TFormComponents>,\n UnwrapOrAny<TRenderProps>\n >['render'] {\n return (innerProps) => render({ ...props, ...innerProps })\n }\n\n function withFieldGroup<\n TFieldGroupData,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n defaultValues,\n }: WithFieldGroupProps<\n TFieldGroupData,\n TComponents,\n TFormComponents,\n TSubmitMeta,\n TRenderProps\n >): <\n TFormData,\n TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TFormSubmitMeta,\n >(\n params: PropsWithChildren<\n NoInfer<TRenderProps> & {\n form:\n | AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n | AppFieldExtendedReactFieldGroupApi<\n // Since this only occurs if you nest it within other field groups, it can be more\n // lenient with the types.\n unknown,\n TFormData,\n string | FieldsMap<unknown, TFormData>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n fields: TFields\n }\n >,\n ) => ReturnType<FunctionComponent> {\n return function Render(innerProps) {\n const fieldGroupProps = useMemo(() => {\n return {\n form: innerProps.form,\n fields: innerProps.fields,\n defaultValues,\n formComponents,\n }\n }, [innerProps.form, innerProps.fields])\n const fieldGroupApi = useFieldGroup(fieldGroupProps as any)\n\n return render({ ...props, ...innerProps, group: fieldGroupApi as any })\n }\n }\n\n /**\n * ⚠️ **Use withForm whenever possible.**\n *\n * Gets a typed form from the `<form.AppForm />` context.\n */\n function useTypedAppFormContext<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n >(\n _props: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ): AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents\n > {\n const form = useFormContext()\n\n return form as never\n }\n\n return {\n useAppForm,\n withForm,\n withFieldGroup,\n useTypedAppFormContext,\n }\n}\n"],"names":["fieldContext","formContext","AppField"],"mappings":";;;;;;;AA2BA;AACA;AAuCA;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAeF;AAEO;AACL;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAAO;AA2BT;AACF;AA+JO;AAGL;AACA;AACAA;AACAC;AAEF;AACE;AA4CE;AAGA;AACE;AACE;AAC+C;AAEjD;AAGF;AACE;AACE;AAEM;AAAA;AAIA;AAEJ;AAiBJ;AAAOC;AAGT;AACE;AAA2B;AACzB;AACA;AACG;AACJ;AAGH;AAAO;AAGT;AAcE;AACA;AACA;AAqCA;AAAyD;AAG3D;AAIE;AACA;AACA;AACA;AAmEA;AACE;AACE;AAAO;AACY;AACE;AACnB;AACA;AAAA;AAGJ;AAEA;AAAsE;AACxE;AAQF;AA4CE;AAEA;AAAO;AAGT;AAAO;AACL;AACA;AACA;AACA;AAEJ;;;"}
@@ -35,10 +35,11 @@ import { AppFieldExtendedReactFieldGroupApi } from './useFieldGroup.cjs';
35
35
  */
36
36
  type UnwrapOrAny<T> = [unknown] extends [T] ? any : T;
37
37
  type UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T] ? [T] extends [DefaultT] ? any : T : T;
38
+ declare function useFormContext(): ReactFormExtendedApi<Record<string, never>, any, any, any, any, any, any, any, any, any, any, any>;
38
39
  export declare function createFormHookContexts(): {
39
40
  fieldContext: Context<AnyFieldApi>;
40
41
  useFieldContext: <TData>() => FieldApi<any, string, TData, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
41
- useFormContext: () => ReactFormExtendedApi<Record<string, never>, any, any, any, any, any, any, any, any, any, any, any>;
42
+ useFormContext: typeof useFormContext;
42
43
  formContext: Context<AnyFormApi>;
43
44
  };
44
45
  interface CreateFormHookProps<TFieldComponents extends Record<string, ComponentType<any>>, TFormComponents extends Record<string, ComponentType<any>>> {
@@ -73,5 +74,6 @@ export declare function createFormHook<const TComponents extends Record<string,
73
74
  form: AppFieldExtendedReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta, TComponents, TFormComponents> | AppFieldExtendedReactFieldGroupApi<unknown, TFormData, string | FieldsMap<unknown, TFormData>, any, any, any, any, any, any, any, any, any, any, unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta, TComponents, TFormComponents>;
74
75
  fields: TFields;
75
76
  }>) => ReturnType<FunctionComponent>;
77
+ useTypedAppFormContext: <TFormData, TOnMount extends undefined | FormValidateOrFn<TFormData>, TOnChange extends undefined | FormValidateOrFn<TFormData>, TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnBlur extends undefined | FormValidateOrFn<TFormData>, TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnSubmit extends undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnDynamic extends undefined | FormValidateOrFn<TFormData>, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>, TSubmitMeta>(_props: FormOptions<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta>) => AppFieldExtendedReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta, TComponents, TFormComponents>;
76
78
  };
77
79
  export {};
@@ -6,6 +6,7 @@ const createFormHook = require("./createFormHook.cjs");
6
6
  const useField = require("./useField.cjs");
7
7
  const useFieldGroup = require("./useFieldGroup.cjs");
8
8
  const useForm = require("./useForm.cjs");
9
+ const useIsomorphicLayoutEffect = require("./useIsomorphicLayoutEffect.cjs");
9
10
  Object.defineProperty(exports, "useStore", {
10
11
  enumerable: true,
11
12
  get: () => reactStore.useStore
@@ -16,6 +17,7 @@ exports.Field = useField.Field;
16
17
  exports.useField = useField.useField;
17
18
  exports.useFieldGroup = useFieldGroup.useFieldGroup;
18
19
  exports.useForm = useForm.useForm;
20
+ exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect.useIsomorphicLayoutEffect;
19
21
  Object.keys(formCore).forEach((k) => {
20
22
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
21
23
  enumerable: true,
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -5,3 +5,4 @@ export * from './types.cjs';
5
5
  export * from './useField.cjs';
6
6
  export * from './useFieldGroup.cjs';
7
7
  export * from './useForm.cjs';
8
+ export * from './useIsomorphicLayoutEffect.cjs';
@@ -60,6 +60,15 @@ function useForm(opts) {
60
60
  useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
61
61
  formApi.update(opts);
62
62
  });
63
+ const hasRan = React.useRef(false);
64
+ useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
65
+ if (!hasRan.current) return;
66
+ if (!opts?.transform) return;
67
+ formCore.mergeAndUpdate(formApi, opts.transform);
68
+ }, [formApi, opts?.transform]);
69
+ useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
70
+ hasRan.current = true;
71
+ });
63
72
  return extendedFormApi;
64
73
  }
65
74
  exports.useForm = useForm;
@@ -1 +1 @@
1
- {"version":3,"file":"useForm.cjs","sources":["../../src/useForm.tsx"],"sourcesContent":["'use client'\n\nimport { FormApi, functionalUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport { useMemo, useState } from 'react'\nimport { Field } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport { useFormId } from './useFormId'\nimport type {\n AnyFormApi,\n AnyFormState,\n FormAsyncValidateOrFn,\n FormOptions,\n FormState,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type { FunctionComponent, PropsWithChildren, ReactNode } from 'react'\nimport type { FieldComponent } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n in out TFormData,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n /**\n * A `Subscribe` function that allows you to listen and react to changes in the form's state. It's especially useful when you need to execute side effects or render specific components in response to state updates.\n */\n Subscribe: <\n TSelected = NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n >(props: {\n selector?: (\n state: NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n ) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReturnType<FunctionComponent>\n}\n\n/**\n * An extended version of the `FormApi` class that includes React-specific functionalities from `ReactFormApi`\n */\nexport type ReactFormExtendedApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n> = FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n ReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n\nfunction LocalSubscribe({\n form,\n selector,\n children,\n}: PropsWithChildren<{\n form: AnyFormApi\n selector: (state: AnyFormState) => AnyFormState\n}>): ReturnType<FunctionComponent> {\n const data = useStore(form.store, selector)\n\n return <>{functionalUpdate(children, data)}</>\n}\n\n/**\n * A custom React Hook that returns an extended instance of the `FormApi` class.\n *\n * This API encapsulates all the necessary functionalities related to the form. It allows you to manage form state, handle submissions, and interact with form fields\n */\nexport function useForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n>(\n opts?: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n) {\n const fallbackFormId = useFormId()\n const [prevFormId, setPrevFormId] = useState<string>(opts?.formId as never)\n\n const [formApi, setFormApi] = useState(() => {\n return new FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >({ ...opts, formId: opts?.formId ?? fallbackFormId })\n })\n\n if (prevFormId !== opts?.formId) {\n const formId = opts?.formId ?? fallbackFormId\n setFormApi(new FormApi({ ...opts, formId }))\n setPrevFormId(formId)\n }\n\n const extendedFormApi = useMemo(() => {\n const extendedApi: ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n > = {\n ...formApi,\n handleSubmit: ((...props: never[]) => {\n return formApi._handleSubmit(...props)\n }) as typeof formApi.handleSubmit,\n // We must add all `get`ters from `core`'s `FormApi` here, as otherwise the spread operator won't catch those\n get formId(): string {\n return formApi._formId\n },\n get state() {\n return formApi.store.state\n },\n } as never\n\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={formApi} />\n }\n\n extendedApi.Subscribe = function Subscribe(props: any) {\n return (\n <LocalSubscribe\n form={formApi}\n selector={props.selector}\n children={props.children}\n />\n )\n }\n\n return extendedApi\n }, [formApi])\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n /**\n * formApi.update should not have any side effects. Think of it like a `useRef`\n * that we need to keep updated every render with the most up-to-date information.\n */\n useIsomorphicLayoutEffect(() => {\n formApi.update(opts)\n })\n\n return extendedFormApi\n}\n"],"names":["jsx","useIsomorphicLayoutEffect"],"mappings":";;;;;;;;;;AA4IA;AAAwB;AACtB;AACA;AAEF;AAIE;AAEA;AACF;AAOO;AA6BL;AACA;AAEA;AACE;AAaqD;AAGvD;AACE;AACA;AACA;AAAoB;AAGtB;AACE;AAaI;AACC;AAED;AAAqC;AACvC;AAAA;AAGE;AAAe;AACjB;AAEE;AAAqB;AACvB;AAGF;AACE;AAAwC;AAG1C;AACE;AACEA;AAAC;AAAA;AACO;AACU;AACA;AAAA;AAAA;AAKtB;AAAO;AAGTC;AAMAA;AACE;AAAmB;AAGrB;AACF;;"}
1
+ {"version":3,"file":"useForm.cjs","sources":["../../src/useForm.tsx"],"sourcesContent":["'use client'\n\nimport { FormApi, functionalUpdate, mergeAndUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport { useMemo, useRef, useState } from 'react'\nimport { Field } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport { useFormId } from './useFormId'\nimport type {\n AnyFormApi,\n AnyFormState,\n FormAsyncValidateOrFn,\n FormOptions,\n FormState,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type { FunctionComponent, PropsWithChildren, ReactNode } from 'react'\nimport type { FieldComponent } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n in out TFormData,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n /**\n * A `Subscribe` function that allows you to listen and react to changes in the form's state. It's especially useful when you need to execute side effects or render specific components in response to state updates.\n */\n Subscribe: <\n TSelected = NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n >(props: {\n selector?: (\n state: NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n ) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReturnType<FunctionComponent>\n}\n\n/**\n * An extended version of the `FormApi` class that includes React-specific functionalities from `ReactFormApi`\n */\nexport type ReactFormExtendedApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n> = FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n ReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n\nfunction LocalSubscribe({\n form,\n selector,\n children,\n}: PropsWithChildren<{\n form: AnyFormApi\n selector: (state: AnyFormState) => AnyFormState\n}>): ReturnType<FunctionComponent> {\n const data = useStore(form.store, selector)\n\n return <>{functionalUpdate(children, data)}</>\n}\n\n/**\n * A custom React Hook that returns an extended instance of the `FormApi` class.\n *\n * This API encapsulates all the necessary functionalities related to the form. It allows you to manage form state, handle submissions, and interact with form fields\n */\nexport function useForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n>(\n opts?: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n) {\n const fallbackFormId = useFormId()\n const [prevFormId, setPrevFormId] = useState<string>(opts?.formId as never)\n\n const [formApi, setFormApi] = useState(() => {\n return new FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >({ ...opts, formId: opts?.formId ?? fallbackFormId })\n })\n\n if (prevFormId !== opts?.formId) {\n const formId = opts?.formId ?? fallbackFormId\n setFormApi(new FormApi({ ...opts, formId }))\n setPrevFormId(formId)\n }\n\n const extendedFormApi = useMemo(() => {\n const extendedApi: ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n > = {\n ...formApi,\n handleSubmit: ((...props: never[]) => {\n return formApi._handleSubmit(...props)\n }) as typeof formApi.handleSubmit,\n // We must add all `get`ters from `core`'s `FormApi` here, as otherwise the spread operator won't catch those\n get formId(): string {\n return formApi._formId\n },\n get state() {\n return formApi.store.state\n },\n } as never\n\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={formApi} />\n }\n\n extendedApi.Subscribe = function Subscribe(props: any) {\n return (\n <LocalSubscribe\n form={formApi}\n selector={props.selector}\n children={props.children}\n />\n )\n }\n\n return extendedApi\n }, [formApi])\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n /**\n * formApi.update should not have any side effects. Think of it like a `useRef`\n * that we need to keep updated every render with the most up-to-date information.\n */\n useIsomorphicLayoutEffect(() => {\n formApi.update(opts)\n })\n\n const hasRan = useRef(false)\n\n useIsomorphicLayoutEffect(() => {\n if (!hasRan.current) return\n if (!opts?.transform) return\n mergeAndUpdate(formApi, opts.transform as never)\n }, [formApi, opts?.transform])\n\n useIsomorphicLayoutEffect(() => {\n hasRan.current = true\n })\n\n return extendedFormApi\n}\n"],"names":["jsx","useIsomorphicLayoutEffect","mergeAndUpdate"],"mappings":";;;;;;;;;;AA4IA;AAAwB;AACtB;AACA;AAEF;AAIE;AAEA;AACF;AAOO;AA6BL;AACA;AAEA;AACE;AAaqD;AAGvD;AACE;AACA;AACA;AAAoB;AAGtB;AACE;AAaI;AACC;AAED;AAAqC;AACvC;AAAA;AAGE;AAAe;AACjB;AAEE;AAAqB;AACvB;AAGF;AACE;AAAwC;AAG1C;AACE;AACEA;AAAC;AAAA;AACO;AACU;AACA;AAAA;AAAA;AAKtB;AAAO;AAGTC;AAMAA;AACE;AAAmB;AAGrB;AAEAA;AACE;AACA;AACAC;AAA+C;AAGjDD;AACE;AAAiB;AAGnB;AACF;;"}
@@ -19,6 +19,7 @@ function _interopNamespaceDefault(e) {
19
19
  return Object.freeze(n);
20
20
  }
21
21
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
22
- const useFormId = React__namespace.version.split(".")[0] === "17" ? useUUID.useUUID : React__namespace.useId;
22
+ const _React = React__namespace;
23
+ const useFormId = React__namespace.version.split(".")[0] === "17" ? useUUID.useUUID : _React.useId;
23
24
  exports.useFormId = useFormId;
24
25
  //# sourceMappingURL=useFormId.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFormId.cjs","sources":["../../src/useFormId.ts"],"sourcesContent":["import * as React from 'react'\nimport { useUUID } from './useUUID'\n\n/** React 17 does not have the useId hook, so we use a random uuid as a fallback. */\nexport const useFormId =\n React.version.split('.')[0] === '17' ? useUUID : React.useId\n"],"names":["React","useUUID"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAIO,MAAM,YACXA,iBAAM,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM,OAAOC,QAAAA,UAAUD,iBAAM;;"}
1
+ {"version":3,"file":"useFormId.cjs","sources":["../../src/useFormId.ts"],"sourcesContent":["import * as React from 'react'\nimport { useUUID } from './useUUID'\n\n/**\n * React 17 does not have the useId hook, so we use a random uuid as a fallback.\n * This is needed to avoid bundlers trying to import non-existing export.\n * Read more: https://github.com/webpack/webpack/issues/14814\n */\nconst _React = React\nexport const useFormId =\n React.version.split('.')[0] === '17' ? useUUID : _React.useId\n"],"names":["React","useUUID"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAQA,MAAM,SAASA;AACR,MAAM,YACXA,iBAAM,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM,OAAOC,QAAAA,UAAU,OAAO;;"}
@@ -1,3 +1,2 @@
1
1
  import { useUUID } from './useUUID.cjs';
2
- /** React 17 does not have the useId hook, so we use a random uuid as a fallback. */
3
2
  export declare const useFormId: typeof useUUID;
@@ -35,10 +35,11 @@ import { AppFieldExtendedReactFieldGroupApi } from './useFieldGroup.js';
35
35
  */
36
36
  type UnwrapOrAny<T> = [unknown] extends [T] ? any : T;
37
37
  type UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T] ? [T] extends [DefaultT] ? any : T : T;
38
+ declare function useFormContext(): ReactFormExtendedApi<Record<string, never>, any, any, any, any, any, any, any, any, any, any, any>;
38
39
  export declare function createFormHookContexts(): {
39
40
  fieldContext: Context<AnyFieldApi>;
40
41
  useFieldContext: <TData>() => FieldApi<any, string, TData, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
41
- useFormContext: () => ReactFormExtendedApi<Record<string, never>, any, any, any, any, any, any, any, any, any, any, any>;
42
+ useFormContext: typeof useFormContext;
42
43
  formContext: Context<AnyFormApi>;
43
44
  };
44
45
  interface CreateFormHookProps<TFieldComponents extends Record<string, ComponentType<any>>, TFormComponents extends Record<string, ComponentType<any>>> {
@@ -73,5 +74,6 @@ export declare function createFormHook<const TComponents extends Record<string,
73
74
  form: AppFieldExtendedReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta, TComponents, TFormComponents> | AppFieldExtendedReactFieldGroupApi<unknown, TFormData, string | FieldsMap<unknown, TFormData>, any, any, any, any, any, any, any, any, any, any, unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta, TComponents, TFormComponents>;
74
75
  fields: TFields;
75
76
  }>) => ReturnType<FunctionComponent>;
77
+ useTypedAppFormContext: <TFormData, TOnMount extends undefined | FormValidateOrFn<TFormData>, TOnChange extends undefined | FormValidateOrFn<TFormData>, TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnBlur extends undefined | FormValidateOrFn<TFormData>, TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnSubmit extends undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnDynamic extends undefined | FormValidateOrFn<TFormData>, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>, TSubmitMeta>(_props: FormOptions<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta>) => AppFieldExtendedReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta, TComponents, TFormComponents>;
76
78
  };
77
79
  export {};
@@ -5,6 +5,15 @@ import { useForm } from "./useForm.js";
5
5
  import { useFieldGroup } from "./useFieldGroup.js";
6
6
  const fieldContext = createContext(null);
7
7
  const formContext = createContext(null);
8
+ function useFormContext() {
9
+ const form = useContext(formContext);
10
+ if (!form) {
11
+ throw new Error(
12
+ "`formContext` only works when within a `formComponent` passed to `createFormHook`"
13
+ );
14
+ }
15
+ return form;
16
+ }
8
17
  function createFormHookContexts() {
9
18
  function useFieldContext() {
10
19
  const field = useContext(fieldContext);
@@ -15,15 +24,6 @@ function createFormHookContexts() {
15
24
  }
16
25
  return field;
17
26
  }
18
- function useFormContext() {
19
- const form = useContext(formContext);
20
- if (!form) {
21
- throw new Error(
22
- "`formContext` only works when within a `formComponent` passed to `createFormHook`"
23
- );
24
- }
25
- return form;
26
- }
27
27
  return { fieldContext, useFieldContext, useFormContext, formContext };
28
28
  }
29
29
  function createFormHook({
@@ -81,10 +81,15 @@ function createFormHook({
81
81
  return render({ ...props, ...innerProps, group: fieldGroupApi });
82
82
  };
83
83
  }
84
+ function useTypedAppFormContext(_props) {
85
+ const form = useFormContext();
86
+ return form;
87
+ }
84
88
  return {
85
89
  useAppForm,
86
90
  withForm,
87
- withFieldGroup
91
+ withFieldGroup,
92
+ useTypedAppFormContext
88
93
  };
89
94
  }
90
95
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"createFormHook.js","sources":["../../src/createFormHook.tsx"],"sourcesContent":["'use client'\n/* eslint-disable @eslint-react/no-context-provider */\nimport { createContext, useContext, useMemo } from 'react'\nimport { useForm } from './useForm'\nimport { useFieldGroup } from './useFieldGroup'\nimport type {\n AnyFieldApi,\n AnyFormApi,\n BaseFormOptions,\n DeepKeysOfType,\n FieldApi,\n FieldsMap,\n FormAsyncValidateOrFn,\n FormOptions,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type {\n ComponentType,\n Context,\n FunctionComponent,\n PropsWithChildren,\n} from 'react'\nimport type { FieldComponent } from './useField'\nimport type { ReactFormExtendedApi } from './useForm'\nimport type { AppFieldExtendedReactFieldGroupApi } from './useFieldGroup'\n\n// We should never hit the `null` case here\nconst fieldContext = createContext<AnyFieldApi>(null as never)\nconst formContext = createContext<AnyFormApi>(null as never)\n\n/**\n * TypeScript inferencing is weird.\n *\n * If you have:\n *\n * @example\n *\n * interface Args<T> {\n * arg?: T\n * }\n *\n * function test<T>(arg?: Partial<Args<T>>): T {\n * return 0 as any;\n * }\n *\n * const a = test({});\n *\n * Then `T` will default to `unknown`.\n *\n * However, if we change `test` to be:\n *\n * @example\n *\n * function test<T extends undefined>(arg?: Partial<Args<T>>): T;\n *\n * Then `T` becomes `undefined`.\n *\n * Here, we are checking if the passed type `T` extends `DefaultT` and **only**\n * `DefaultT`, as if that's the case we assume that inferencing has not occurred.\n */\ntype UnwrapOrAny<T> = [unknown] extends [T] ? any : T\ntype UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T]\n ? [T] extends [DefaultT]\n ? any\n : T\n : T\n\nexport function createFormHookContexts() {\n function useFieldContext<TData>() {\n const field = useContext(fieldContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!field) {\n throw new Error(\n '`fieldContext` only works when within a `fieldComponent` passed to `createFormHook`',\n )\n }\n\n return field as FieldApi<\n any,\n string,\n TData,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n }\n\n function useFormContext() {\n const form = useContext(formContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!form) {\n throw new Error(\n '`formContext` only works when within a `formComponent` passed to `createFormHook`',\n )\n }\n\n return form as ReactFormExtendedApi<\n // If you need access to the form data, you need to use `withForm` instead\n Record<string, never>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n }\n\n return { fieldContext, useFieldContext, useFormContext, formContext }\n}\n\ninterface CreateFormHookProps<\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> {\n fieldComponents: TFieldComponents\n fieldContext: Context<AnyFieldApi>\n formComponents: TFormComponents\n formContext: Context<AnyFormApi>\n}\n\n/**\n * @private\n */\nexport type AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> = ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n NoInfer<TFormComponents> & {\n AppField: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n NoInfer<TFieldComponents>\n >\n AppForm: ComponentType<\n // PropsWithChildren<P> is not optional in React 17\n PropsWithChildren<{}>\n >\n }\n\nexport interface WithFormProps<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TRenderProps extends object = Record<string, never>,\n> extends FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n form: AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport interface WithFieldGroupProps<\n TFieldGroupData,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TSubmitMeta,\n TRenderProps extends object = Record<string, never>,\n> extends BaseFormOptions<TFieldGroupData, TSubmitMeta> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n group: AppFieldExtendedReactFieldGroupApi<\n unknown,\n TFieldGroupData,\n string | FieldsMap<unknown, TFieldGroupData>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n // this types it as 'never' in the render prop. It should prevent any\n // untyped meta passed to the handleSubmit by accident.\n unknown extends TSubmitMeta ? never : TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport function createFormHook<\n const TComponents extends Record<string, ComponentType<any>>,\n const TFormComponents extends Record<string, ComponentType<any>>,\n>({\n fieldComponents,\n fieldContext,\n formContext,\n formComponents,\n}: CreateFormHookProps<TComponents, TFormComponents>) {\n function useAppForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n >(\n props: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ): AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents\n > {\n const form = useForm(props)\n\n // PropsWithChildren<P> is not optional in React 17\n const AppForm = useMemo<ComponentType<PropsWithChildren<{}>>>(() => {\n return ({ children }) => {\n return (\n <formContext.Provider value={form}>{children}</formContext.Provider>\n )\n }\n }, [form])\n\n const AppField = useMemo(() => {\n const AppField = (({ children, ...props }) => {\n return (\n <form.Field {...props}>\n {(field) => (\n // eslint-disable-next-line @eslint-react/no-context-provider\n <fieldContext.Provider value={field}>\n {children(Object.assign(field, fieldComponents))}\n </fieldContext.Provider>\n )}\n </form.Field>\n )\n }) as FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents\n >\n return AppField\n }, [form])\n\n const extendedForm = useMemo(() => {\n return Object.assign(form, {\n AppField,\n AppForm,\n ...formComponents,\n })\n }, [form, AppField, AppForm])\n\n return extendedForm\n }\n\n function withForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n }: WithFormProps<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents,\n TRenderProps\n >): WithFormProps<\n UnwrapOrAny<TFormData>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnDynamic>,\n UnwrapDefaultOrAny<\n undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync\n >,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>,\n UnwrapOrAny<TSubmitMeta>,\n UnwrapOrAny<TComponents>,\n UnwrapOrAny<TFormComponents>,\n UnwrapOrAny<TRenderProps>\n >['render'] {\n return (innerProps) => render({ ...props, ...innerProps })\n }\n\n function withFieldGroup<\n TFieldGroupData,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n defaultValues,\n }: WithFieldGroupProps<\n TFieldGroupData,\n TComponents,\n TFormComponents,\n TSubmitMeta,\n TRenderProps\n >): <\n TFormData,\n TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TFormSubmitMeta,\n >(\n params: PropsWithChildren<\n NoInfer<TRenderProps> & {\n form:\n | AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n | AppFieldExtendedReactFieldGroupApi<\n // Since this only occurs if you nest it within other field groups, it can be more\n // lenient with the types.\n unknown,\n TFormData,\n string | FieldsMap<unknown, TFormData>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n fields: TFields\n }\n >,\n ) => ReturnType<FunctionComponent> {\n return function Render(innerProps) {\n const fieldGroupProps = useMemo(() => {\n return {\n form: innerProps.form,\n fields: innerProps.fields,\n defaultValues,\n formComponents,\n }\n }, [innerProps.form, innerProps.fields])\n const fieldGroupApi = useFieldGroup(fieldGroupProps as any)\n\n return render({ ...props, ...innerProps, group: fieldGroupApi as any })\n }\n }\n\n return {\n useAppForm,\n withForm,\n withFieldGroup,\n }\n}\n"],"names":["fieldContext","formContext","AppField"],"mappings":";;;;;AA2BA;AACA;AAuCO;AACL;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAAO;AA2BT;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAAO;AAiBT;AACF;AA+JO;AAGL;AACA;AACAA;AACAC;AAEF;AACE;AA4CE;AAGA;AACE;AACE;AAC+C;AAEjD;AAGF;AACE;AACE;AAEM;AAAA;AAIA;AAEJ;AAiBJ;AAAOC;AAGT;AACE;AAA2B;AACzB;AACA;AACG;AACJ;AAGH;AAAO;AAGT;AAcE;AACA;AACA;AAqCA;AAAyD;AAG3D;AAIE;AACA;AACA;AACA;AAmEA;AACE;AACE;AAAO;AACY;AACE;AACnB;AACA;AAAA;AAGJ;AAEA;AAAsE;AACxE;AAGF;AAAO;AACL;AACA;AACA;AAEJ;;;;;"}
1
+ {"version":3,"file":"createFormHook.js","sources":["../../src/createFormHook.tsx"],"sourcesContent":["'use client'\n/* eslint-disable @eslint-react/no-context-provider */\nimport { createContext, useContext, useMemo } from 'react'\nimport { useForm } from './useForm'\nimport { useFieldGroup } from './useFieldGroup'\nimport type {\n AnyFieldApi,\n AnyFormApi,\n BaseFormOptions,\n DeepKeysOfType,\n FieldApi,\n FieldsMap,\n FormAsyncValidateOrFn,\n FormOptions,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type {\n ComponentType,\n Context,\n FunctionComponent,\n PropsWithChildren,\n} from 'react'\nimport type { FieldComponent } from './useField'\nimport type { ReactFormExtendedApi } from './useForm'\nimport type { AppFieldExtendedReactFieldGroupApi } from './useFieldGroup'\n\n// We should never hit the `null` case here\nconst fieldContext = createContext<AnyFieldApi>(null as never)\nconst formContext = createContext<AnyFormApi>(null as never)\n\n/**\n * TypeScript inferencing is weird.\n *\n * If you have:\n *\n * @example\n *\n * interface Args<T> {\n * arg?: T\n * }\n *\n * function test<T>(arg?: Partial<Args<T>>): T {\n * return 0 as any;\n * }\n *\n * const a = test({});\n *\n * Then `T` will default to `unknown`.\n *\n * However, if we change `test` to be:\n *\n * @example\n *\n * function test<T extends undefined>(arg?: Partial<Args<T>>): T;\n *\n * Then `T` becomes `undefined`.\n *\n * Here, we are checking if the passed type `T` extends `DefaultT` and **only**\n * `DefaultT`, as if that's the case we assume that inferencing has not occurred.\n */\ntype UnwrapOrAny<T> = [unknown] extends [T] ? any : T\ntype UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T]\n ? [T] extends [DefaultT]\n ? any\n : T\n : T\n\nfunction useFormContext() {\n const form = useContext(formContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!form) {\n throw new Error(\n '`formContext` only works when within a `formComponent` passed to `createFormHook`',\n )\n }\n\n return form as ReactFormExtendedApi<\n // If you need access to the form data, you need to use `withForm` instead\n Record<string, never>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n}\n\nexport function createFormHookContexts() {\n function useFieldContext<TData>() {\n const field = useContext(fieldContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!field) {\n throw new Error(\n '`fieldContext` only works when within a `fieldComponent` passed to `createFormHook`',\n )\n }\n\n return field as FieldApi<\n any,\n string,\n TData,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n }\n\n return { fieldContext, useFieldContext, useFormContext, formContext }\n}\n\ninterface CreateFormHookProps<\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> {\n fieldComponents: TFieldComponents\n fieldContext: Context<AnyFieldApi>\n formComponents: TFormComponents\n formContext: Context<AnyFormApi>\n}\n\n/**\n * @private\n */\nexport type AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n> = ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n NoInfer<TFormComponents> & {\n AppField: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n NoInfer<TFieldComponents>\n >\n AppForm: ComponentType<\n // PropsWithChildren<P> is not optional in React 17\n PropsWithChildren<{}>\n >\n }\n\nexport interface WithFormProps<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TRenderProps extends object = Record<string, never>,\n> extends FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n form: AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport interface WithFieldGroupProps<\n TFieldGroupData,\n TFieldComponents extends Record<string, ComponentType<any>>,\n TFormComponents extends Record<string, ComponentType<any>>,\n TSubmitMeta,\n TRenderProps extends object = Record<string, never>,\n> extends BaseFormOptions<TFieldGroupData, TSubmitMeta> {\n // Optional, but adds props to the `render` function outside of `form`\n props?: TRenderProps\n render: FunctionComponent<\n PropsWithChildren<\n NoInfer<TRenderProps> & {\n group: AppFieldExtendedReactFieldGroupApi<\n unknown,\n TFieldGroupData,\n string | FieldsMap<unknown, TFieldGroupData>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n undefined | FormAsyncValidateOrFn<unknown>,\n // this types it as 'never' in the render prop. It should prevent any\n // untyped meta passed to the handleSubmit by accident.\n unknown extends TSubmitMeta ? never : TSubmitMeta,\n TFieldComponents,\n TFormComponents\n >\n }\n >\n >\n}\n\nexport function createFormHook<\n const TComponents extends Record<string, ComponentType<any>>,\n const TFormComponents extends Record<string, ComponentType<any>>,\n>({\n fieldComponents,\n fieldContext,\n formContext,\n formComponents,\n}: CreateFormHookProps<TComponents, TFormComponents>) {\n function useAppForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n >(\n props: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ): AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents\n > {\n const form = useForm(props)\n\n // PropsWithChildren<P> is not optional in React 17\n const AppForm = useMemo<ComponentType<PropsWithChildren<{}>>>(() => {\n return ({ children }) => {\n return (\n <formContext.Provider value={form}>{children}</formContext.Provider>\n )\n }\n }, [form])\n\n const AppField = useMemo(() => {\n const AppField = (({ children, ...props }) => {\n return (\n <form.Field {...props}>\n {(field) => (\n // eslint-disable-next-line @eslint-react/no-context-provider\n <fieldContext.Provider value={field}>\n {children(Object.assign(field, fieldComponents))}\n </fieldContext.Provider>\n )}\n </form.Field>\n )\n }) as FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents\n >\n return AppField\n }, [form])\n\n const extendedForm = useMemo(() => {\n return Object.assign(form, {\n AppField,\n AppForm,\n ...formComponents,\n })\n }, [form, AppField, AppForm])\n\n return extendedForm\n }\n\n function withForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n }: WithFormProps<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents,\n TRenderProps\n >): WithFormProps<\n UnwrapOrAny<TFormData>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnDynamic>,\n UnwrapDefaultOrAny<\n undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync\n >,\n UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>,\n UnwrapOrAny<TSubmitMeta>,\n UnwrapOrAny<TComponents>,\n UnwrapOrAny<TFormComponents>,\n UnwrapOrAny<TRenderProps>\n >['render'] {\n return (innerProps) => render({ ...props, ...innerProps })\n }\n\n function withFieldGroup<\n TFieldGroupData,\n TSubmitMeta,\n TRenderProps extends object = {},\n >({\n render,\n props,\n defaultValues,\n }: WithFieldGroupProps<\n TFieldGroupData,\n TComponents,\n TFormComponents,\n TSubmitMeta,\n TRenderProps\n >): <\n TFormData,\n TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TFormSubmitMeta,\n >(\n params: PropsWithChildren<\n NoInfer<TRenderProps> & {\n form:\n | AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n | AppFieldExtendedReactFieldGroupApi<\n // Since this only occurs if you nest it within other field groups, it can be more\n // lenient with the types.\n unknown,\n TFormData,\n string | FieldsMap<unknown, TFormData>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta,\n TComponents,\n TFormComponents\n >\n fields: TFields\n }\n >,\n ) => ReturnType<FunctionComponent> {\n return function Render(innerProps) {\n const fieldGroupProps = useMemo(() => {\n return {\n form: innerProps.form,\n fields: innerProps.fields,\n defaultValues,\n formComponents,\n }\n }, [innerProps.form, innerProps.fields])\n const fieldGroupApi = useFieldGroup(fieldGroupProps as any)\n\n return render({ ...props, ...innerProps, group: fieldGroupApi as any })\n }\n }\n\n /**\n * ⚠️ **Use withForm whenever possible.**\n *\n * Gets a typed form from the `<form.AppForm />` context.\n */\n function useTypedAppFormContext<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n >(\n _props: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ): AppFieldExtendedReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta,\n TComponents,\n TFormComponents\n > {\n const form = useFormContext()\n\n return form as never\n }\n\n return {\n useAppForm,\n withForm,\n withFieldGroup,\n useTypedAppFormContext,\n }\n}\n"],"names":["fieldContext","formContext","AppField"],"mappings":";;;;;AA2BA;AACA;AAuCA;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAeF;AAEO;AACL;AACE;AAGA;AACE;AAAU;AACR;AAAA;AAIJ;AAAO;AA2BT;AACF;AA+JO;AAGL;AACA;AACAA;AACAC;AAEF;AACE;AA4CE;AAGA;AACE;AACE;AAC+C;AAEjD;AAGF;AACE;AACE;AAEM;AAAA;AAIA;AAEJ;AAiBJ;AAAOC;AAGT;AACE;AAA2B;AACzB;AACA;AACG;AACJ;AAGH;AAAO;AAGT;AAcE;AACA;AACA;AAqCA;AAAyD;AAG3D;AAIE;AACA;AACA;AACA;AAmEA;AACE;AACE;AAAO;AACY;AACE;AACnB;AACA;AAAA;AAGJ;AAEA;AAAsE;AACxE;AAQF;AA4CE;AAEA;AAAO;AAGT;AAAO;AACL;AACA;AACA;AACA;AAEJ;;;;;"}
@@ -5,3 +5,4 @@ export * from './types.js';
5
5
  export * from './useField.js';
6
6
  export * from './useFieldGroup.js';
7
7
  export * from './useForm.js';
8
+ export * from './useIsomorphicLayoutEffect.js';
package/dist/esm/index.js CHANGED
@@ -4,6 +4,7 @@ import { createFormHook, createFormHookContexts } from "./createFormHook.js";
4
4
  import { Field, useField } from "./useField.js";
5
5
  import { useFieldGroup } from "./useFieldGroup.js";
6
6
  import { useForm } from "./useForm.js";
7
+ import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
7
8
  export {
8
9
  Field,
9
10
  createFormHook,
@@ -11,6 +12,7 @@ export {
11
12
  useField,
12
13
  useFieldGroup,
13
14
  useForm,
15
+ useIsomorphicLayoutEffect,
14
16
  useStore
15
17
  };
16
18
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, Fragment } from "react/jsx-runtime";
3
- import { FormApi, functionalUpdate } from "@tanstack/form-core";
3
+ import { FormApi, mergeAndUpdate, functionalUpdate } from "@tanstack/form-core";
4
4
  import { useStore } from "@tanstack/react-store";
5
- import { useState, useMemo } from "react";
5
+ import { useState, useMemo, useRef } from "react";
6
6
  import { Field } from "./useField.js";
7
7
  import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
8
8
  import { useFormId } from "./useFormId.js";
@@ -58,6 +58,15 @@ function useForm(opts) {
58
58
  useIsomorphicLayoutEffect(() => {
59
59
  formApi.update(opts);
60
60
  });
61
+ const hasRan = useRef(false);
62
+ useIsomorphicLayoutEffect(() => {
63
+ if (!hasRan.current) return;
64
+ if (!opts?.transform) return;
65
+ mergeAndUpdate(formApi, opts.transform);
66
+ }, [formApi, opts?.transform]);
67
+ useIsomorphicLayoutEffect(() => {
68
+ hasRan.current = true;
69
+ });
61
70
  return extendedFormApi;
62
71
  }
63
72
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"useForm.js","sources":["../../src/useForm.tsx"],"sourcesContent":["'use client'\n\nimport { FormApi, functionalUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport { useMemo, useState } from 'react'\nimport { Field } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport { useFormId } from './useFormId'\nimport type {\n AnyFormApi,\n AnyFormState,\n FormAsyncValidateOrFn,\n FormOptions,\n FormState,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type { FunctionComponent, PropsWithChildren, ReactNode } from 'react'\nimport type { FieldComponent } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n in out TFormData,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n /**\n * A `Subscribe` function that allows you to listen and react to changes in the form's state. It's especially useful when you need to execute side effects or render specific components in response to state updates.\n */\n Subscribe: <\n TSelected = NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n >(props: {\n selector?: (\n state: NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n ) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReturnType<FunctionComponent>\n}\n\n/**\n * An extended version of the `FormApi` class that includes React-specific functionalities from `ReactFormApi`\n */\nexport type ReactFormExtendedApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n> = FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n ReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n\nfunction LocalSubscribe({\n form,\n selector,\n children,\n}: PropsWithChildren<{\n form: AnyFormApi\n selector: (state: AnyFormState) => AnyFormState\n}>): ReturnType<FunctionComponent> {\n const data = useStore(form.store, selector)\n\n return <>{functionalUpdate(children, data)}</>\n}\n\n/**\n * A custom React Hook that returns an extended instance of the `FormApi` class.\n *\n * This API encapsulates all the necessary functionalities related to the form. It allows you to manage form state, handle submissions, and interact with form fields\n */\nexport function useForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n>(\n opts?: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n) {\n const fallbackFormId = useFormId()\n const [prevFormId, setPrevFormId] = useState<string>(opts?.formId as never)\n\n const [formApi, setFormApi] = useState(() => {\n return new FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >({ ...opts, formId: opts?.formId ?? fallbackFormId })\n })\n\n if (prevFormId !== opts?.formId) {\n const formId = opts?.formId ?? fallbackFormId\n setFormApi(new FormApi({ ...opts, formId }))\n setPrevFormId(formId)\n }\n\n const extendedFormApi = useMemo(() => {\n const extendedApi: ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n > = {\n ...formApi,\n handleSubmit: ((...props: never[]) => {\n return formApi._handleSubmit(...props)\n }) as typeof formApi.handleSubmit,\n // We must add all `get`ters from `core`'s `FormApi` here, as otherwise the spread operator won't catch those\n get formId(): string {\n return formApi._formId\n },\n get state() {\n return formApi.store.state\n },\n } as never\n\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={formApi} />\n }\n\n extendedApi.Subscribe = function Subscribe(props: any) {\n return (\n <LocalSubscribe\n form={formApi}\n selector={props.selector}\n children={props.children}\n />\n )\n }\n\n return extendedApi\n }, [formApi])\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n /**\n * formApi.update should not have any side effects. Think of it like a `useRef`\n * that we need to keep updated every render with the most up-to-date information.\n */\n useIsomorphicLayoutEffect(() => {\n formApi.update(opts)\n })\n\n return extendedFormApi\n}\n"],"names":[],"mappings":";;;;;;;;AA4IA;AAAwB;AACtB;AACA;AAEF;AAIE;AAEA;AACF;AAOO;AA6BL;AACA;AAEA;AACE;AAaqD;AAGvD;AACE;AACA;AACA;AAAoB;AAGtB;AACE;AAaI;AACC;AAED;AAAqC;AACvC;AAAA;AAGE;AAAe;AACjB;AAEE;AAAqB;AACvB;AAGF;AACE;AAAwC;AAG1C;AACE;AACE;AAAC;AAAA;AACO;AACU;AACA;AAAA;AAAA;AAKtB;AAAO;AAGT;AAMA;AACE;AAAmB;AAGrB;AACF;;;;"}
1
+ {"version":3,"file":"useForm.js","sources":["../../src/useForm.tsx"],"sourcesContent":["'use client'\n\nimport { FormApi, functionalUpdate, mergeAndUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport { useMemo, useRef, useState } from 'react'\nimport { Field } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport { useFormId } from './useFormId'\nimport type {\n AnyFormApi,\n AnyFormState,\n FormAsyncValidateOrFn,\n FormOptions,\n FormState,\n FormValidateOrFn,\n} from '@tanstack/form-core'\nimport type { FunctionComponent, PropsWithChildren, ReactNode } from 'react'\nimport type { FieldComponent } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n in out TFormData,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n /**\n * A `Subscribe` function that allows you to listen and react to changes in the form's state. It's especially useful when you need to execute side effects or render specific components in response to state updates.\n */\n Subscribe: <\n TSelected = NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n >(props: {\n selector?: (\n state: NoInfer<\n FormState<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer\n >\n >,\n ) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReturnType<FunctionComponent>\n}\n\n/**\n * An extended version of the `FormApi` class that includes React-specific functionalities from `ReactFormApi`\n */\nexport type ReactFormExtendedApi<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n> = FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n> &\n ReactFormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n\nfunction LocalSubscribe({\n form,\n selector,\n children,\n}: PropsWithChildren<{\n form: AnyFormApi\n selector: (state: AnyFormState) => AnyFormState\n}>): ReturnType<FunctionComponent> {\n const data = useStore(form.store, selector)\n\n return <>{functionalUpdate(children, data)}</>\n}\n\n/**\n * A custom React Hook that returns an extended instance of the `FormApi` class.\n *\n * This API encapsulates all the necessary functionalities related to the form. It allows you to manage form state, handle submissions, and interact with form fields\n */\nexport function useForm<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n>(\n opts?: FormOptions<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n) {\n const fallbackFormId = useFormId()\n const [prevFormId, setPrevFormId] = useState<string>(opts?.formId as never)\n\n const [formApi, setFormApi] = useState(() => {\n return new FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >({ ...opts, formId: opts?.formId ?? fallbackFormId })\n })\n\n if (prevFormId !== opts?.formId) {\n const formId = opts?.formId ?? fallbackFormId\n setFormApi(new FormApi({ ...opts, formId }))\n setPrevFormId(formId)\n }\n\n const extendedFormApi = useMemo(() => {\n const extendedApi: ReactFormExtendedApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n > = {\n ...formApi,\n handleSubmit: ((...props: never[]) => {\n return formApi._handleSubmit(...props)\n }) as typeof formApi.handleSubmit,\n // We must add all `get`ters from `core`'s `FormApi` here, as otherwise the spread operator won't catch those\n get formId(): string {\n return formApi._formId\n },\n get state() {\n return formApi.store.state\n },\n } as never\n\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={formApi} />\n }\n\n extendedApi.Subscribe = function Subscribe(props: any) {\n return (\n <LocalSubscribe\n form={formApi}\n selector={props.selector}\n children={props.children}\n />\n )\n }\n\n return extendedApi\n }, [formApi])\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n /**\n * formApi.update should not have any side effects. Think of it like a `useRef`\n * that we need to keep updated every render with the most up-to-date information.\n */\n useIsomorphicLayoutEffect(() => {\n formApi.update(opts)\n })\n\n const hasRan = useRef(false)\n\n useIsomorphicLayoutEffect(() => {\n if (!hasRan.current) return\n if (!opts?.transform) return\n mergeAndUpdate(formApi, opts.transform as never)\n }, [formApi, opts?.transform])\n\n useIsomorphicLayoutEffect(() => {\n hasRan.current = true\n })\n\n return extendedFormApi\n}\n"],"names":[],"mappings":";;;;;;;;AA4IA;AAAwB;AACtB;AACA;AAEF;AAIE;AAEA;AACF;AAOO;AA6BL;AACA;AAEA;AACE;AAaqD;AAGvD;AACE;AACA;AACA;AAAoB;AAGtB;AACE;AAaI;AACC;AAED;AAAqC;AACvC;AAAA;AAGE;AAAe;AACjB;AAEE;AAAqB;AACvB;AAGF;AACE;AAAwC;AAG1C;AACE;AACE;AAAC;AAAA;AACO;AACU;AACA;AAAA;AAAA;AAKtB;AAAO;AAGT;AAMA;AACE;AAAmB;AAGrB;AAEA;AACE;AACA;AACA;AAA+C;AAGjD;AACE;AAAiB;AAGnB;AACF;;;;"}
@@ -1,3 +1,2 @@
1
1
  import { useUUID } from './useUUID.js';
2
- /** React 17 does not have the useId hook, so we use a random uuid as a fallback. */
3
2
  export declare const useFormId: typeof useUUID;
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { useUUID } from "./useUUID.js";
3
- const useFormId = React.version.split(".")[0] === "17" ? useUUID : React.useId;
3
+ const _React = React;
4
+ const useFormId = React.version.split(".")[0] === "17" ? useUUID : _React.useId;
4
5
  export {
5
6
  useFormId
6
7
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useFormId.js","sources":["../../src/useFormId.ts"],"sourcesContent":["import * as React from 'react'\nimport { useUUID } from './useUUID'\n\n/** React 17 does not have the useId hook, so we use a random uuid as a fallback. */\nexport const useFormId =\n React.version.split('.')[0] === '17' ? useUUID : React.useId\n"],"names":[],"mappings":";;AAIO,MAAM,YACX,MAAM,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM,OAAO,UAAU,MAAM;"}
1
+ {"version":3,"file":"useFormId.js","sources":["../../src/useFormId.ts"],"sourcesContent":["import * as React from 'react'\nimport { useUUID } from './useUUID'\n\n/**\n * React 17 does not have the useId hook, so we use a random uuid as a fallback.\n * This is needed to avoid bundlers trying to import non-existing export.\n * Read more: https://github.com/webpack/webpack/issues/14814\n */\nconst _React = React\nexport const useFormId =\n React.version.split('.')[0] === '17' ? useUUID : _React.useId\n"],"names":[],"mappings":";;AAQA,MAAM,SAAS;AACR,MAAM,YACX,MAAM,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM,OAAO,UAAU,OAAO;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-form",
3
- "version": "1.27.7",
3
+ "version": "1.28.1",
4
4
  "description": "Powerful, type-safe forms for React.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "dependencies": {
40
40
  "@tanstack/react-store": "^0.8.0",
41
- "@tanstack/form-core": "1.27.7"
41
+ "@tanstack/form-core": "1.28.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "^19.0.7",
@@ -65,6 +65,33 @@ type UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T]
65
65
  : T
66
66
  : T
67
67
 
68
+ function useFormContext() {
69
+ const form = useContext(formContext)
70
+
71
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
72
+ if (!form) {
73
+ throw new Error(
74
+ '`formContext` only works when within a `formComponent` passed to `createFormHook`',
75
+ )
76
+ }
77
+
78
+ return form as ReactFormExtendedApi<
79
+ // If you need access to the form data, you need to use `withForm` instead
80
+ Record<string, never>,
81
+ any,
82
+ any,
83
+ any,
84
+ any,
85
+ any,
86
+ any,
87
+ any,
88
+ any,
89
+ any,
90
+ any,
91
+ any
92
+ >
93
+ }
94
+
68
95
  export function createFormHookContexts() {
69
96
  function useFieldContext<TData>() {
70
97
  const field = useContext(fieldContext)
@@ -103,33 +130,6 @@ export function createFormHookContexts() {
103
130
  >
104
131
  }
105
132
 
106
- function useFormContext() {
107
- const form = useContext(formContext)
108
-
109
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
110
- if (!form) {
111
- throw new Error(
112
- '`formContext` only works when within a `formComponent` passed to `createFormHook`',
113
- )
114
- }
115
-
116
- return form as ReactFormExtendedApi<
117
- // If you need access to the form data, you need to use `withForm` instead
118
- Record<string, never>,
119
- any,
120
- any,
121
- any,
122
- any,
123
- any,
124
- any,
125
- any,
126
- any,
127
- any,
128
- any,
129
- any
130
- >
131
- }
132
-
133
133
  return { fieldContext, useFieldContext, useFormContext, formContext }
134
134
  }
135
135
 
@@ -540,9 +540,64 @@ export function createFormHook<
540
540
  }
541
541
  }
542
542
 
543
+ /**
544
+ * ⚠️ **Use withForm whenever possible.**
545
+ *
546
+ * Gets a typed form from the `<form.AppForm />` context.
547
+ */
548
+ function useTypedAppFormContext<
549
+ TFormData,
550
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
551
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
552
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
553
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
554
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
555
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
556
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
557
+ TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
558
+ TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
559
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
560
+ TSubmitMeta,
561
+ >(
562
+ _props: FormOptions<
563
+ TFormData,
564
+ TOnMount,
565
+ TOnChange,
566
+ TOnChangeAsync,
567
+ TOnBlur,
568
+ TOnBlurAsync,
569
+ TOnSubmit,
570
+ TOnSubmitAsync,
571
+ TOnDynamic,
572
+ TOnDynamicAsync,
573
+ TOnServer,
574
+ TSubmitMeta
575
+ >,
576
+ ): AppFieldExtendedReactFormApi<
577
+ TFormData,
578
+ TOnMount,
579
+ TOnChange,
580
+ TOnChangeAsync,
581
+ TOnBlur,
582
+ TOnBlurAsync,
583
+ TOnSubmit,
584
+ TOnSubmitAsync,
585
+ TOnDynamic,
586
+ TOnDynamicAsync,
587
+ TOnServer,
588
+ TSubmitMeta,
589
+ TComponents,
590
+ TFormComponents
591
+ > {
592
+ const form = useFormContext()
593
+
594
+ return form as never
595
+ }
596
+
543
597
  return {
544
598
  useAppForm,
545
599
  withForm,
546
600
  withFieldGroup,
601
+ useTypedAppFormContext,
547
602
  }
548
603
  }
package/src/index.ts CHANGED
@@ -7,3 +7,4 @@ export * from './types'
7
7
  export * from './useField'
8
8
  export * from './useFieldGroup'
9
9
  export * from './useForm'
10
+ export * from './useIsomorphicLayoutEffect'
package/src/useForm.tsx CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client'
2
2
 
3
- import { FormApi, functionalUpdate } from '@tanstack/form-core'
3
+ import { FormApi, functionalUpdate, mergeAndUpdate } from '@tanstack/form-core'
4
4
  import { useStore } from '@tanstack/react-store'
5
- import { useMemo, useState } from 'react'
5
+ import { useMemo, useRef, useState } from 'react'
6
6
  import { Field } from './useField'
7
7
  import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
8
8
  import { useFormId } from './useFormId'
@@ -266,5 +266,17 @@ export function useForm<
266
266
  formApi.update(opts)
267
267
  })
268
268
 
269
+ const hasRan = useRef(false)
270
+
271
+ useIsomorphicLayoutEffect(() => {
272
+ if (!hasRan.current) return
273
+ if (!opts?.transform) return
274
+ mergeAndUpdate(formApi, opts.transform as never)
275
+ }, [formApi, opts?.transform])
276
+
277
+ useIsomorphicLayoutEffect(() => {
278
+ hasRan.current = true
279
+ })
280
+
269
281
  return extendedFormApi
270
282
  }
package/src/useFormId.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import * as React from 'react'
2
2
  import { useUUID } from './useUUID'
3
3
 
4
- /** React 17 does not have the useId hook, so we use a random uuid as a fallback. */
4
+ /**
5
+ * React 17 does not have the useId hook, so we use a random uuid as a fallback.
6
+ * This is needed to avoid bundlers trying to import non-existing export.
7
+ * Read more: https://github.com/webpack/webpack/issues/14814
8
+ */
9
+ const _React = React
5
10
  export const useFormId =
6
- React.version.split('.')[0] === '17' ? useUUID : React.useId
11
+ React.version.split('.')[0] === '17' ? useUUID : _React.useId