@tanstack/react-form 1.28.0 → 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.
@@ -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;
@@ -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.28.0",
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.28.0"
41
+ "@tanstack/form-core": "1.28.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "^19.0.7",
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