@tanstack/react-form 0.38.0 → 0.39.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.
@@ -1,9 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const formCore = require("@tanstack/form-core");
4
+ const reactStore = require("@tanstack/react-store");
4
5
  const useForm = require("./useForm.cjs");
5
6
  const useField = require("./useField.cjs");
6
7
  const useTransform = require("./useTransform.cjs");
8
+ Object.defineProperty(exports, "useStore", {
9
+ enumerable: true,
10
+ get: () => reactStore.useStore
11
+ });
7
12
  exports.useForm = useForm.useForm;
8
13
  exports.Field = useField.Field;
9
14
  exports.useField = useField.useField;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
@@ -1,4 +1,5 @@
1
1
  export * from '@tanstack/form-core';
2
+ export { useStore } from '@tanstack/react-store';
2
3
  export type { ReactFormApi, ReactFormExtendedApi } from './useForm.cjs';
3
4
  export { useForm } from './useForm.cjs';
4
5
  export type { UseField, FieldComponent } from './useField.cjs';
@@ -33,7 +33,17 @@ const Field = ({
33
33
  ...fieldOptions
34
34
  }) => {
35
35
  const fieldApi = useField(fieldOptions);
36
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: formCore.functionalUpdate(children, fieldApi) });
36
+ const jsxToDisplay = react.useMemo(
37
+ () => formCore.functionalUpdate(children, fieldApi),
38
+ /**
39
+ * The reason this exists is to fix an issue with the React Compiler.
40
+ * Namely, functionalUpdate is memoized where it checks for `fieldApi`, which is a static type.
41
+ * This means that when `state.value` changes, it does not trigger a re-render. The useMemo explicitly fixes this problem
42
+ */
43
+ // eslint-disable-next-line react-hooks/exhaustive-deps
44
+ [children, fieldApi, fieldApi.state.value, fieldApi.state.meta]
45
+ );
46
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxToDisplay });
37
47
  };
38
48
  exports.Field = Field;
39
49
  exports.useField = useField;
@@ -1 +1 @@
1
- {"version":3,"file":"useField.cjs","sources":["../../src/useField.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { useStore } from '@tanstack/react-store'\nimport { FieldApi, functionalUpdate } from '@tanstack/form-core'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { FunctionComponent, ReactNode } from 'react'\nimport type { UseFieldOptions } from './types'\nimport type { DeepKeys, DeepValue, Validator } from '@tanstack/form-core'\n\ninterface ReactFieldApi<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> {\n /**\n * A pre-bound and type-safe sub-field component using this field as a root.\n */\n Field: FieldComponent<TParentData, TFormValidator>\n}\n\n/**\n * A type representing a hook for using a field in a form with the given form data type.\n *\n * A function that takes an optional object with a `name` property and field options, and returns a `FieldApi` instance for the specified field.\n */\nexport type UseField<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: Omit<\n UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>,\n 'form'\n >,\n) => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A hook for managing a field in a form.\n * @param opts An object with field options.\n *\n * @returns The `FieldApi` instance for the specified field.\n */\nexport function useField<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: UseFieldOptions<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n) {\n const [fieldApi] = useState(() => {\n const api = new FieldApi({\n ...opts,\n form: opts.form,\n name: opts.name,\n })\n\n const extendedApi: typeof api & ReactFieldApi<TParentData, TFormValidator> =\n api as never\n\n extendedApi.Field = Field as never\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi])\n\n /**\n * fieldApi.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 fieldApi.update(opts)\n })\n\n useStore(\n fieldApi.store,\n opts.mode === 'array'\n ? (state) => {\n return [state.meta, Object.keys(state.value ?? []).length]\n }\n : undefined,\n )\n\n return fieldApi\n}\n\n/**\n * @param children A render function that takes a field API instance and returns a React element.\n */\ntype FieldComponentProps<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = {\n children: (\n fieldApi: FieldApi<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n ) => ReactNode\n} & UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A type alias representing a field component for a specific form data type.\n */\nexport type FieldComponent<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: Omit<\n FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n 'form'\n>) => ReactNode\n\n/**\n * A function component that takes field options and a render function as children and returns a React component.\n *\n * The `Field` component uses the `useField` hook internally to manage the field instance.\n */\nexport const Field = (<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n>): ReactNode => {\n const fieldApi = useField(fieldOptions as any)\n\n return (<>{functionalUpdate(children, fieldApi as any)}</>) as never\n}) satisfies FunctionComponent<FieldComponentProps<any, any, any, any, any>>\n"],"names":["useState","FieldApi","useIsomorphicLayoutEffect","useStore","jsx","Fragment","functionalUpdate"],"mappings":";;;;;;;AAiDO,SAAS,SAWd,MAOA;AACA,QAAM,CAAC,QAAQ,IAAIA,MAAAA,SAAS,MAAM;AAC1B,UAAA,MAAM,IAAIC,kBAAS;AAAA,MACvB,GAAG;AAAA,MACH,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,IAAA,CACZ;AAED,UAAM,cACJ;AAEF,gBAAY,QAAQ;AAEb,WAAA;AAAA,EAAA,CACR;AAEDC,4BAAAA,0BAA0B,SAAS,OAAO,CAAC,QAAQ,CAAC;AAMpDA,4BAAAA,0BAA0B,MAAM;AAC9B,aAAS,OAAO,IAAI;AAAA,EAAA,CACrB;AAEDC,aAAA;AAAA,IACE,SAAS;AAAA,IACT,KAAK,SAAS,UACV,CAAC,UAAU;AACF,aAAA,CAAC,MAAM,MAAM,OAAO,KAAK,MAAM,SAAS,EAAE,EAAE,MAAM;AAAA,IAAA,IAE3D;AAAA,EACN;AAEO,SAAA;AACT;AA4DO,MAAM,QAAS,CAUpB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAMiB;AACT,QAAA,WAAW,SAAS,YAAmB;AAE7C,SAAWC,2BAAAA,IAAAC,WAAAA,UAAA,EAAA,UAAAC,SAAAA,iBAAiB,UAAU,QAAe,GAAE;AACzD;;;"}
1
+ {"version":3,"file":"useField.cjs","sources":["../../src/useField.tsx"],"sourcesContent":["import React, { useMemo, useState } from 'react'\nimport { useStore } from '@tanstack/react-store'\nimport { FieldApi, functionalUpdate } from '@tanstack/form-core'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { FunctionComponent, ReactNode } from 'react'\nimport type { UseFieldOptions } from './types'\nimport type { DeepKeys, DeepValue, Validator } from '@tanstack/form-core'\n\ninterface ReactFieldApi<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> {\n /**\n * A pre-bound and type-safe sub-field component using this field as a root.\n */\n Field: FieldComponent<TParentData, TFormValidator>\n}\n\n/**\n * A type representing a hook for using a field in a form with the given form data type.\n *\n * A function that takes an optional object with a `name` property and field options, and returns a `FieldApi` instance for the specified field.\n */\nexport type UseField<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: Omit<\n UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>,\n 'form'\n >,\n) => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A hook for managing a field in a form.\n * @param opts An object with field options.\n *\n * @returns The `FieldApi` instance for the specified field.\n */\nexport function useField<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: UseFieldOptions<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n) {\n const [fieldApi] = useState(() => {\n const api = new FieldApi({\n ...opts,\n form: opts.form,\n name: opts.name,\n })\n\n const extendedApi: typeof api & ReactFieldApi<TParentData, TFormValidator> =\n api as never\n\n extendedApi.Field = Field as never\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi])\n\n /**\n * fieldApi.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 fieldApi.update(opts)\n })\n\n useStore(\n fieldApi.store,\n opts.mode === 'array'\n ? (state) => {\n return [state.meta, Object.keys(state.value ?? []).length]\n }\n : undefined,\n )\n\n return fieldApi\n}\n\n/**\n * @param children A render function that takes a field API instance and returns a React element.\n */\ntype FieldComponentProps<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = {\n children: (\n fieldApi: FieldApi<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n ) => ReactNode\n} & UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A type alias representing a field component for a specific form data type.\n */\nexport type FieldComponent<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: Omit<\n FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n 'form'\n>) => ReactNode\n\n/**\n * A function component that takes field options and a render function as children and returns a React component.\n *\n * The `Field` component uses the `useField` hook internally to manage the field instance.\n */\nexport const Field = (<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n>): ReactNode => {\n const fieldApi = useField(fieldOptions as any)\n\n const jsxToDisplay = useMemo(\n () => functionalUpdate(children, fieldApi as any),\n /**\n * The reason this exists is to fix an issue with the React Compiler.\n * Namely, functionalUpdate is memoized where it checks for `fieldApi`, which is a static type.\n * This means that when `state.value` changes, it does not trigger a re-render. The useMemo explicitly fixes this problem\n */\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [children, fieldApi, fieldApi.state.value, fieldApi.state.meta],\n )\n return (<>{jsxToDisplay}</>) as never\n}) satisfies FunctionComponent<FieldComponentProps<any, any, any, any, any>>\n"],"names":["useState","FieldApi","useIsomorphicLayoutEffect","useStore","useMemo","functionalUpdate"],"mappings":";;;;;;;AAiDO,SAAS,SAWd,MAOA;AACA,QAAM,CAAC,QAAQ,IAAIA,MAAAA,SAAS,MAAM;AAC1B,UAAA,MAAM,IAAIC,kBAAS;AAAA,MACvB,GAAG;AAAA,MACH,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,IAAA,CACZ;AAED,UAAM,cACJ;AAEF,gBAAY,QAAQ;AAEb,WAAA;AAAA,EAAA,CACR;AAEDC,4BAAAA,0BAA0B,SAAS,OAAO,CAAC,QAAQ,CAAC;AAMpDA,4BAAAA,0BAA0B,MAAM;AAC9B,aAAS,OAAO,IAAI;AAAA,EAAA,CACrB;AAEDC,aAAA;AAAA,IACE,SAAS;AAAA,IACT,KAAK,SAAS,UACV,CAAC,UAAU;AACF,aAAA,CAAC,MAAM,MAAM,OAAO,KAAK,MAAM,SAAS,EAAE,EAAE,MAAM;AAAA,IAAA,IAE3D;AAAA,EACN;AAEO,SAAA;AACT;AA4DO,MAAM,QAAS,CAUpB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAMiB;AACT,QAAA,WAAW,SAAS,YAAmB;AAE7C,QAAM,eAAeC,MAAA;AAAA,IACnB,MAAMC,SAAiB,iBAAA,UAAU,QAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOhD,CAAC,UAAU,UAAU,SAAS,MAAM,OAAO,SAAS,MAAM,IAAI;AAAA,EAChE;AACA,+DAAW,UAAa,aAAA,CAAA;AAC1B;;;"}
@@ -6,6 +6,14 @@ const reactStore = require("@tanstack/react-store");
6
6
  const react = require("react");
7
7
  const useField = require("./useField.cjs");
8
8
  const useIsomorphicLayoutEffect = require("./useIsomorphicLayoutEffect.cjs");
9
+ function LocalSubscribe({
10
+ form,
11
+ selector,
12
+ children
13
+ }) {
14
+ const data = reactStore.useStore(form.store, selector);
15
+ return formCore.functionalUpdate(children, data);
16
+ }
9
17
  function useForm(opts) {
10
18
  const [formApi] = react.useState(() => {
11
19
  const api = new formCore.FormApi(opts);
@@ -13,21 +21,20 @@ function useForm(opts) {
13
21
  extendedApi.Field = function APIField(props) {
14
22
  return /* @__PURE__ */ jsxRuntime.jsx(useField.Field, { ...props, form: api });
15
23
  };
16
- extendedApi.useField = (props) => useField.useField({ ...props, form: api });
17
- extendedApi.useStore = (selector) => {
18
- return reactStore.useStore(api.store, selector);
19
- };
20
24
  extendedApi.Subscribe = (props) => {
21
- return formCore.functionalUpdate(
22
- props.children,
23
- // eslint-disable-next-line react-hooks/rules-of-hooks
24
- reactStore.useStore(api.store, props.selector)
25
+ return /* @__PURE__ */ jsxRuntime.jsx(
26
+ LocalSubscribe,
27
+ {
28
+ form: api,
29
+ selector: props.selector,
30
+ children: props.children
31
+ }
25
32
  );
26
33
  };
27
34
  return extendedApi;
28
35
  });
29
36
  useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(formApi.mount, []);
30
- formApi.useStore((state) => state.isSubmitting);
37
+ reactStore.useStore(formApi.store, (state) => state.isSubmitting);
31
38
  useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
32
39
  formApi.update(opts);
33
40
  });
@@ -1 +1 @@
1
- {"version":3,"file":"useForm.cjs","sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, functionalUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport React, { useState } from 'react'\nimport { Field, useField } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { ReactNode } from 'react'\nimport type { FieldComponent, UseField } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\nimport type { FormOptions, FormState, Validator } from '@tanstack/form-core'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<TFormData, TFormValidator>\n /**\n * A custom React hook that provides functionalities related to individual form fields. It gives you access to field values, errors, and allows you to set or update field values.\n */\n useField: UseField<TFormData, TFormValidator>\n /**\n * A `useStore` hook that connects to the internal store of the form. It can be used to access the form's current state or any other related state information. You can optionally pass in a selector function to cherry-pick specific parts of the state\n */\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\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: <TSelected = NoInfer<FormState<TFormData>>>(props: {\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReactNode\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> = FormApi<TFormData, TFormValidator> & ReactFormApi<TFormData, TFormValidator>\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(opts?: FormOptions<TFormData, TFormValidator>) {\n const [formApi] = useState(() => {\n const api = new FormApi<TFormData, TFormValidator>(opts)\n\n const extendedApi: ReactFormExtendedApi<TFormData, TFormValidator> =\n api as never\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={api} />\n }\n // eslint-disable-next-line react-hooks/rules-of-hooks\n extendedApi.useField = (props) => useField({ ...props, form: api })\n extendedApi.useStore = (selector) => {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useStore(api.store, selector)\n }\n extendedApi.Subscribe = (props) => {\n return functionalUpdate(\n props.children,\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useStore(api.store, props.selector),\n )\n }\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n formApi.useStore((state) => state.isSubmitting)\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 formApi\n}\n"],"names":["useState","FormApi","jsx","Field","useField","useStore","functionalUpdate","useIsomorphicLayoutEffect"],"mappings":";;;;;;;;AAqDO,SAAS,QAGd,MAA+C;AAC/C,QAAM,CAAC,OAAO,IAAIA,MAAAA,SAAS,MAAM;AACzB,UAAA,MAAM,IAAIC,SAAA,QAAmC,IAAI;AAEvD,UAAM,cACJ;AACU,gBAAA,QAAQ,SAAS,SAAS,OAAO;AAC3C,aAAQC,2BAAAA,IAAAC,SAAAA,OAAA,EAAO,GAAG,OAAO,MAAM,KAAK;AAAA,IACtC;AAEY,gBAAA,WAAW,CAAC,UAAUC,SAAAA,SAAS,EAAE,GAAG,OAAO,MAAM,KAAK;AACtD,gBAAA,WAAW,CAAC,aAAa;AAE5B,aAAAC,oBAAS,IAAI,OAAO,QAAQ;AAAA,IACrC;AACY,gBAAA,YAAY,CAAC,UAAU;AAC1B,aAAAC,SAAA;AAAA,QACL,MAAM;AAAA;AAAA,QAEND,WAAAA,SAAS,IAAI,OAAO,MAAM,QAAQ;AAAA,MACpC;AAAA,IACF;AAEO,WAAA;AAAA,EAAA,CACR;AAEyBE,sDAAA,QAAQ,OAAO,EAAE;AAE3C,UAAQ,SAAS,CAAC,UAAU,MAAM,YAAY;AAM9CA,4BAAAA,0BAA0B,MAAM;AAC9B,YAAQ,OAAO,IAAI;AAAA,EAAA,CACpB;AAEM,SAAA;AACT;;"}
1
+ {"version":3,"file":"useForm.cjs","sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, functionalUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport React, { useState } from 'react'\nimport { Field } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { PropsWithChildren, ReactNode } from 'react'\nimport type { FieldComponent, UseField } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\nimport type { FormOptions, FormState, Validator } from '@tanstack/form-core'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<TFormData, TFormValidator>\n /**\n * A custom React hook that provides functionalities related to individual form fields. It gives you access to field values, errors, and allows you to set or update field values.\n */\n useField: UseField<TFormData, TFormValidator>\n /**\n * A `useStore` hook that connects to the internal store of the form. It can be used to access the form's current state or any other related state information. You can optionally pass in a selector function to cherry-pick specific parts of the state\n */\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\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: <TSelected = NoInfer<FormState<TFormData>>>(props: {\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReactNode\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> = FormApi<TFormData, TFormValidator> & ReactFormApi<TFormData, TFormValidator>\n\nfunction LocalSubscribe({\n form,\n selector,\n children,\n}: PropsWithChildren<{\n form: FormApi<any, any>\n selector: (state: FormState<any>) => FormState<any>\n}>) {\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(opts?: FormOptions<TFormData, TFormValidator>) {\n const [formApi] = useState(() => {\n const api = new FormApi<TFormData, TFormValidator>(opts)\n\n const extendedApi: ReactFormExtendedApi<TFormData, TFormValidator> =\n api as never\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={api} />\n }\n extendedApi.Subscribe = (props: any) => {\n return (\n <LocalSubscribe\n form={api}\n selector={props.selector}\n children={props.children}\n />\n )\n }\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n useStore(formApi.store, (state) => state.isSubmitting)\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 formApi\n}\n"],"names":["useStore","functionalUpdate","useState","FormApi","jsx","Field","useIsomorphicLayoutEffect"],"mappings":";;;;;;;;AAgDA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF,GAGI;AACF,QAAM,OAAOA,WAAA,SAAS,KAAK,OAAO,QAAQ;AAEnC,SAAAC,SAAA,iBAAiB,UAAU,IAAI;AACxC;AAOO,SAAS,QAGd,MAA+C;AAC/C,QAAM,CAAC,OAAO,IAAIC,MAAAA,SAAS,MAAM;AACzB,UAAA,MAAM,IAAIC,SAAA,QAAmC,IAAI;AAEvD,UAAM,cACJ;AACU,gBAAA,QAAQ,SAAS,SAAS,OAAO;AAC3C,aAAQC,2BAAAA,IAAAC,SAAAA,OAAA,EAAO,GAAG,OAAO,MAAM,KAAK;AAAA,IACtC;AACY,gBAAA,YAAY,CAAC,UAAe;AAEpC,aAAAD,2BAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAM;AAAA,UACN,UAAU,MAAM;AAAA,UAChB,UAAU,MAAM;AAAA,QAAA;AAAA,MAClB;AAAA,IAEJ;AAEO,WAAA;AAAA,EAAA,CACR;AAEyBE,sDAAA,QAAQ,OAAO,EAAE;AAE3CN,aAAA,SAAS,QAAQ,OAAO,CAAC,UAAU,MAAM,YAAY;AAMrDM,4BAAAA,0BAA0B,MAAM;AAC9B,YAAQ,OAAO,IAAI;AAAA,EAAA,CACpB;AAEM,SAAA;AACT;;"}
@@ -1,4 +1,5 @@
1
1
  export * from '@tanstack/form-core';
2
+ export { useStore } from '@tanstack/react-store';
2
3
  export type { ReactFormApi, ReactFormExtendedApi } from './useForm.js';
3
4
  export { useForm } from './useForm.js';
4
5
  export type { UseField, FieldComponent } from './useField.js';
package/dist/esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "@tanstack/form-core";
2
+ import { useStore } from "@tanstack/react-store";
2
3
  import { useForm } from "./useForm.js";
3
4
  import { Field, useField } from "./useField.js";
4
5
  import { useTransform } from "./useTransform.js";
@@ -6,6 +7,7 @@ export {
6
7
  Field,
7
8
  useField,
8
9
  useForm,
10
+ useStore,
9
11
  useTransform
10
12
  };
11
13
  //# 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,5 +1,5 @@
1
1
  import { jsx, Fragment } from "react/jsx-runtime";
2
- import { useState } from "react";
2
+ import { useState, useMemo } from "react";
3
3
  import { useStore } from "@tanstack/react-store";
4
4
  import { FieldApi, functionalUpdate } from "@tanstack/form-core";
5
5
  import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
@@ -31,7 +31,17 @@ const Field = ({
31
31
  ...fieldOptions
32
32
  }) => {
33
33
  const fieldApi = useField(fieldOptions);
34
- return /* @__PURE__ */ jsx(Fragment, { children: functionalUpdate(children, fieldApi) });
34
+ const jsxToDisplay = useMemo(
35
+ () => functionalUpdate(children, fieldApi),
36
+ /**
37
+ * The reason this exists is to fix an issue with the React Compiler.
38
+ * Namely, functionalUpdate is memoized where it checks for `fieldApi`, which is a static type.
39
+ * This means that when `state.value` changes, it does not trigger a re-render. The useMemo explicitly fixes this problem
40
+ */
41
+ // eslint-disable-next-line react-hooks/exhaustive-deps
42
+ [children, fieldApi, fieldApi.state.value, fieldApi.state.meta]
43
+ );
44
+ return /* @__PURE__ */ jsx(Fragment, { children: jsxToDisplay });
35
45
  };
36
46
  export {
37
47
  Field,
@@ -1 +1 @@
1
- {"version":3,"file":"useField.js","sources":["../../src/useField.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { useStore } from '@tanstack/react-store'\nimport { FieldApi, functionalUpdate } from '@tanstack/form-core'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { FunctionComponent, ReactNode } from 'react'\nimport type { UseFieldOptions } from './types'\nimport type { DeepKeys, DeepValue, Validator } from '@tanstack/form-core'\n\ninterface ReactFieldApi<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> {\n /**\n * A pre-bound and type-safe sub-field component using this field as a root.\n */\n Field: FieldComponent<TParentData, TFormValidator>\n}\n\n/**\n * A type representing a hook for using a field in a form with the given form data type.\n *\n * A function that takes an optional object with a `name` property and field options, and returns a `FieldApi` instance for the specified field.\n */\nexport type UseField<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: Omit<\n UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>,\n 'form'\n >,\n) => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A hook for managing a field in a form.\n * @param opts An object with field options.\n *\n * @returns The `FieldApi` instance for the specified field.\n */\nexport function useField<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: UseFieldOptions<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n) {\n const [fieldApi] = useState(() => {\n const api = new FieldApi({\n ...opts,\n form: opts.form,\n name: opts.name,\n })\n\n const extendedApi: typeof api & ReactFieldApi<TParentData, TFormValidator> =\n api as never\n\n extendedApi.Field = Field as never\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi])\n\n /**\n * fieldApi.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 fieldApi.update(opts)\n })\n\n useStore(\n fieldApi.store,\n opts.mode === 'array'\n ? (state) => {\n return [state.meta, Object.keys(state.value ?? []).length]\n }\n : undefined,\n )\n\n return fieldApi\n}\n\n/**\n * @param children A render function that takes a field API instance and returns a React element.\n */\ntype FieldComponentProps<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = {\n children: (\n fieldApi: FieldApi<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n ) => ReactNode\n} & UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A type alias representing a field component for a specific form data type.\n */\nexport type FieldComponent<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: Omit<\n FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n 'form'\n>) => ReactNode\n\n/**\n * A function component that takes field options and a render function as children and returns a React component.\n *\n * The `Field` component uses the `useField` hook internally to manage the field instance.\n */\nexport const Field = (<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n>): ReactNode => {\n const fieldApi = useField(fieldOptions as any)\n\n return (<>{functionalUpdate(children, fieldApi as any)}</>) as never\n}) satisfies FunctionComponent<FieldComponentProps<any, any, any, any, any>>\n"],"names":[],"mappings":";;;;;AAiDO,SAAS,SAWd,MAOA;AACA,QAAM,CAAC,QAAQ,IAAI,SAAS,MAAM;AAC1B,UAAA,MAAM,IAAI,SAAS;AAAA,MACvB,GAAG;AAAA,MACH,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,IAAA,CACZ;AAED,UAAM,cACJ;AAEF,gBAAY,QAAQ;AAEb,WAAA;AAAA,EAAA,CACR;AAED,4BAA0B,SAAS,OAAO,CAAC,QAAQ,CAAC;AAMpD,4BAA0B,MAAM;AAC9B,aAAS,OAAO,IAAI;AAAA,EAAA,CACrB;AAED;AAAA,IACE,SAAS;AAAA,IACT,KAAK,SAAS,UACV,CAAC,UAAU;AACF,aAAA,CAAC,MAAM,MAAM,OAAO,KAAK,MAAM,SAAS,EAAE,EAAE,MAAM;AAAA,IAAA,IAE3D;AAAA,EACN;AAEO,SAAA;AACT;AA4DO,MAAM,QAAS,CAUpB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAMiB;AACT,QAAA,WAAW,SAAS,YAAmB;AAE7C,SAAW,oBAAA,UAAA,EAAA,UAAA,iBAAiB,UAAU,QAAe,GAAE;AACzD;"}
1
+ {"version":3,"file":"useField.js","sources":["../../src/useField.tsx"],"sourcesContent":["import React, { useMemo, useState } from 'react'\nimport { useStore } from '@tanstack/react-store'\nimport { FieldApi, functionalUpdate } from '@tanstack/form-core'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { FunctionComponent, ReactNode } from 'react'\nimport type { UseFieldOptions } from './types'\nimport type { DeepKeys, DeepValue, Validator } from '@tanstack/form-core'\n\ninterface ReactFieldApi<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> {\n /**\n * A pre-bound and type-safe sub-field component using this field as a root.\n */\n Field: FieldComponent<TParentData, TFormValidator>\n}\n\n/**\n * A type representing a hook for using a field in a form with the given form data type.\n *\n * A function that takes an optional object with a `name` property and field options, and returns a `FieldApi` instance for the specified field.\n */\nexport type UseField<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: Omit<\n UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>,\n 'form'\n >,\n) => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A hook for managing a field in a form.\n * @param opts An object with field options.\n *\n * @returns The `FieldApi` instance for the specified field.\n */\nexport function useField<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>(\n opts: UseFieldOptions<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n) {\n const [fieldApi] = useState(() => {\n const api = new FieldApi({\n ...opts,\n form: opts.form,\n name: opts.name,\n })\n\n const extendedApi: typeof api & ReactFieldApi<TParentData, TFormValidator> =\n api as never\n\n extendedApi.Field = Field as never\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi])\n\n /**\n * fieldApi.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 fieldApi.update(opts)\n })\n\n useStore(\n fieldApi.store,\n opts.mode === 'array'\n ? (state) => {\n return [state.meta, Object.keys(state.value ?? []).length]\n }\n : undefined,\n )\n\n return fieldApi\n}\n\n/**\n * @param children A render function that takes a field API instance and returns a React element.\n */\ntype FieldComponentProps<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = {\n children: (\n fieldApi: FieldApi<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n ) => ReactNode\n} & UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>\n\n/**\n * A type alias representing a field component for a specific form data type.\n */\nexport type FieldComponent<\n TParentData,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n> = <\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: Omit<\n FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n >,\n 'form'\n>) => ReactNode\n\n/**\n * A function component that takes field options and a render function as children and returns a React component.\n *\n * The `Field` component uses the `useField` hook internally to manage the field instance.\n */\nexport const Field = (<\n TParentData,\n TName extends DeepKeys<TParentData>,\n TFieldValidator extends\n | Validator<DeepValue<TParentData, TName>, unknown>\n | undefined = undefined,\n TFormValidator extends\n | Validator<TParentData, unknown>\n | undefined = undefined,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n>({\n children,\n ...fieldOptions\n}: FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n>): ReactNode => {\n const fieldApi = useField(fieldOptions as any)\n\n const jsxToDisplay = useMemo(\n () => functionalUpdate(children, fieldApi as any),\n /**\n * The reason this exists is to fix an issue with the React Compiler.\n * Namely, functionalUpdate is memoized where it checks for `fieldApi`, which is a static type.\n * This means that when `state.value` changes, it does not trigger a re-render. The useMemo explicitly fixes this problem\n */\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [children, fieldApi, fieldApi.state.value, fieldApi.state.meta],\n )\n return (<>{jsxToDisplay}</>) as never\n}) satisfies FunctionComponent<FieldComponentProps<any, any, any, any, any>>\n"],"names":[],"mappings":";;;;;AAiDO,SAAS,SAWd,MAOA;AACA,QAAM,CAAC,QAAQ,IAAI,SAAS,MAAM;AAC1B,UAAA,MAAM,IAAI,SAAS;AAAA,MACvB,GAAG;AAAA,MACH,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,IAAA,CACZ;AAED,UAAM,cACJ;AAEF,gBAAY,QAAQ;AAEb,WAAA;AAAA,EAAA,CACR;AAED,4BAA0B,SAAS,OAAO,CAAC,QAAQ,CAAC;AAMpD,4BAA0B,MAAM;AAC9B,aAAS,OAAO,IAAI;AAAA,EAAA,CACrB;AAED;AAAA,IACE,SAAS;AAAA,IACT,KAAK,SAAS,UACV,CAAC,UAAU;AACF,aAAA,CAAC,MAAM,MAAM,OAAO,KAAK,MAAM,SAAS,EAAE,EAAE,MAAM;AAAA,IAAA,IAE3D;AAAA,EACN;AAEO,SAAA;AACT;AA4DO,MAAM,QAAS,CAUpB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAMiB;AACT,QAAA,WAAW,SAAS,YAAmB;AAE7C,QAAM,eAAe;AAAA,IACnB,MAAM,iBAAiB,UAAU,QAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOhD,CAAC,UAAU,UAAU,SAAS,MAAM,OAAO,SAAS,MAAM,IAAI;AAAA,EAChE;AACA,yCAAW,UAAa,aAAA,CAAA;AAC1B;"}
@@ -2,8 +2,16 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { FormApi, functionalUpdate } from "@tanstack/form-core";
3
3
  import { useStore } from "@tanstack/react-store";
4
4
  import { useState } from "react";
5
- import { Field, useField } from "./useField.js";
5
+ import { Field } from "./useField.js";
6
6
  import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
7
+ function LocalSubscribe({
8
+ form,
9
+ selector,
10
+ children
11
+ }) {
12
+ const data = useStore(form.store, selector);
13
+ return functionalUpdate(children, data);
14
+ }
7
15
  function useForm(opts) {
8
16
  const [formApi] = useState(() => {
9
17
  const api = new FormApi(opts);
@@ -11,21 +19,20 @@ function useForm(opts) {
11
19
  extendedApi.Field = function APIField(props) {
12
20
  return /* @__PURE__ */ jsx(Field, { ...props, form: api });
13
21
  };
14
- extendedApi.useField = (props) => useField({ ...props, form: api });
15
- extendedApi.useStore = (selector) => {
16
- return useStore(api.store, selector);
17
- };
18
22
  extendedApi.Subscribe = (props) => {
19
- return functionalUpdate(
20
- props.children,
21
- // eslint-disable-next-line react-hooks/rules-of-hooks
22
- useStore(api.store, props.selector)
23
+ return /* @__PURE__ */ jsx(
24
+ LocalSubscribe,
25
+ {
26
+ form: api,
27
+ selector: props.selector,
28
+ children: props.children
29
+ }
23
30
  );
24
31
  };
25
32
  return extendedApi;
26
33
  });
27
34
  useIsomorphicLayoutEffect(formApi.mount, []);
28
- formApi.useStore((state) => state.isSubmitting);
35
+ useStore(formApi.store, (state) => state.isSubmitting);
29
36
  useIsomorphicLayoutEffect(() => {
30
37
  formApi.update(opts);
31
38
  });
@@ -1 +1 @@
1
- {"version":3,"file":"useForm.js","sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, functionalUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport React, { useState } from 'react'\nimport { Field, useField } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { ReactNode } from 'react'\nimport type { FieldComponent, UseField } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\nimport type { FormOptions, FormState, Validator } from '@tanstack/form-core'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<TFormData, TFormValidator>\n /**\n * A custom React hook that provides functionalities related to individual form fields. It gives you access to field values, errors, and allows you to set or update field values.\n */\n useField: UseField<TFormData, TFormValidator>\n /**\n * A `useStore` hook that connects to the internal store of the form. It can be used to access the form's current state or any other related state information. You can optionally pass in a selector function to cherry-pick specific parts of the state\n */\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\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: <TSelected = NoInfer<FormState<TFormData>>>(props: {\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReactNode\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> = FormApi<TFormData, TFormValidator> & ReactFormApi<TFormData, TFormValidator>\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(opts?: FormOptions<TFormData, TFormValidator>) {\n const [formApi] = useState(() => {\n const api = new FormApi<TFormData, TFormValidator>(opts)\n\n const extendedApi: ReactFormExtendedApi<TFormData, TFormValidator> =\n api as never\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={api} />\n }\n // eslint-disable-next-line react-hooks/rules-of-hooks\n extendedApi.useField = (props) => useField({ ...props, form: api })\n extendedApi.useStore = (selector) => {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useStore(api.store, selector)\n }\n extendedApi.Subscribe = (props) => {\n return functionalUpdate(\n props.children,\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useStore(api.store, props.selector),\n )\n }\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n formApi.useStore((state) => state.isSubmitting)\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 formApi\n}\n"],"names":[],"mappings":";;;;;;AAqDO,SAAS,QAGd,MAA+C;AAC/C,QAAM,CAAC,OAAO,IAAI,SAAS,MAAM;AACzB,UAAA,MAAM,IAAI,QAAmC,IAAI;AAEvD,UAAM,cACJ;AACU,gBAAA,QAAQ,SAAS,SAAS,OAAO;AAC3C,aAAQ,oBAAA,OAAA,EAAO,GAAG,OAAO,MAAM,KAAK;AAAA,IACtC;AAEY,gBAAA,WAAW,CAAC,UAAU,SAAS,EAAE,GAAG,OAAO,MAAM,KAAK;AACtD,gBAAA,WAAW,CAAC,aAAa;AAE5B,aAAA,SAAS,IAAI,OAAO,QAAQ;AAAA,IACrC;AACY,gBAAA,YAAY,CAAC,UAAU;AAC1B,aAAA;AAAA,QACL,MAAM;AAAA;AAAA,QAEN,SAAS,IAAI,OAAO,MAAM,QAAQ;AAAA,MACpC;AAAA,IACF;AAEO,WAAA;AAAA,EAAA,CACR;AAEyB,4BAAA,QAAQ,OAAO,EAAE;AAE3C,UAAQ,SAAS,CAAC,UAAU,MAAM,YAAY;AAM9C,4BAA0B,MAAM;AAC9B,YAAQ,OAAO,IAAI;AAAA,EAAA,CACpB;AAEM,SAAA;AACT;"}
1
+ {"version":3,"file":"useForm.js","sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, functionalUpdate } from '@tanstack/form-core'\nimport { useStore } from '@tanstack/react-store'\nimport React, { useState } from 'react'\nimport { Field } from './useField'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { PropsWithChildren, ReactNode } from 'react'\nimport type { FieldComponent, UseField } from './useField'\nimport type { NoInfer } from '@tanstack/react-store'\nimport type { FormOptions, FormState, Validator } from '@tanstack/form-core'\n\n/**\n * Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`\n */\nexport interface ReactFormApi<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> {\n /**\n * A React component to render form fields. With this, you can render and manage individual form fields.\n */\n Field: FieldComponent<TFormData, TFormValidator>\n /**\n * A custom React hook that provides functionalities related to individual form fields. It gives you access to field values, errors, and allows you to set or update field values.\n */\n useField: UseField<TFormData, TFormValidator>\n /**\n * A `useStore` hook that connects to the internal store of the form. It can be used to access the form's current state or any other related state information. You can optionally pass in a selector function to cherry-pick specific parts of the state\n */\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\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: <TSelected = NoInfer<FormState<TFormData>>>(props: {\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => ReactNode\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> = FormApi<TFormData, TFormValidator> & ReactFormApi<TFormData, TFormValidator>\n\nfunction LocalSubscribe({\n form,\n selector,\n children,\n}: PropsWithChildren<{\n form: FormApi<any, any>\n selector: (state: FormState<any>) => FormState<any>\n}>) {\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 TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(opts?: FormOptions<TFormData, TFormValidator>) {\n const [formApi] = useState(() => {\n const api = new FormApi<TFormData, TFormValidator>(opts)\n\n const extendedApi: ReactFormExtendedApi<TFormData, TFormValidator> =\n api as never\n extendedApi.Field = function APIField(props) {\n return <Field {...props} form={api} />\n }\n extendedApi.Subscribe = (props: any) => {\n return (\n <LocalSubscribe\n form={api}\n selector={props.selector}\n children={props.children}\n />\n )\n }\n\n return extendedApi\n })\n\n useIsomorphicLayoutEffect(formApi.mount, [])\n\n useStore(formApi.store, (state) => state.isSubmitting)\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 formApi\n}\n"],"names":[],"mappings":";;;;;;AAgDA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF,GAGI;AACF,QAAM,OAAO,SAAS,KAAK,OAAO,QAAQ;AAEnC,SAAA,iBAAiB,UAAU,IAAI;AACxC;AAOO,SAAS,QAGd,MAA+C;AAC/C,QAAM,CAAC,OAAO,IAAI,SAAS,MAAM;AACzB,UAAA,MAAM,IAAI,QAAmC,IAAI;AAEvD,UAAM,cACJ;AACU,gBAAA,QAAQ,SAAS,SAAS,OAAO;AAC3C,aAAQ,oBAAA,OAAA,EAAO,GAAG,OAAO,MAAM,KAAK;AAAA,IACtC;AACY,gBAAA,YAAY,CAAC,UAAe;AAEpC,aAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAM;AAAA,UACN,UAAU,MAAM;AAAA,UAChB,UAAU,MAAM;AAAA,QAAA;AAAA,MAClB;AAAA,IAEJ;AAEO,WAAA;AAAA,EAAA,CACR;AAEyB,4BAAA,QAAQ,OAAO,EAAE;AAE3C,WAAS,QAAQ,OAAO,CAAC,UAAU,MAAM,YAAY;AAMrD,4BAA0B,MAAM;AAC9B,YAAQ,OAAO,IAAI;AAAA,EAAA,CACpB;AAEM,SAAA;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-form",
3
- "version": "0.38.0",
3
+ "version": "0.39.1",
4
4
  "description": "Powerful, type-safe forms for React.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -67,19 +67,20 @@
67
67
  "src"
68
68
  ],
69
69
  "dependencies": {
70
- "@remix-run/node": "^2.14.0",
71
- "@tanstack/react-store": "^0.5.6",
70
+ "@remix-run/node": "^2.15.0",
71
+ "@tanstack/react-store": "^0.6.1",
72
72
  "decode-formdata": "^0.8.0",
73
- "@tanstack/form-core": "0.38.0"
73
+ "@tanstack/form-core": "0.39.1"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@tanstack/start": "^1.81.1",
77
77
  "@types/react": "^18.3.3",
78
78
  "@types/react-dom": "^18.3.0",
79
79
  "@vitejs/plugin-react": "^4.3.3",
80
+ "eslint-plugin-react-compiler": "^19.0.0-beta-0dec889-20241115",
80
81
  "react": "^18.3.1",
81
82
  "react-dom": "^18.3.1",
82
- "vite": "^5.4.10"
83
+ "vite": "^5.4.11"
83
84
  },
84
85
  "peerDependencies": {
85
86
  "@tanstack/start": "^1.43.13",
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from '@tanstack/form-core'
2
2
 
3
+ export { useStore } from '@tanstack/react-store'
4
+
3
5
  export type { ReactFormApi, ReactFormExtendedApi } from './useForm'
4
6
  export { useForm } from './useForm'
5
7
 
package/src/useField.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react'
1
+ import React, { useMemo, useState } from 'react'
2
2
  import { useStore } from '@tanstack/react-store'
3
3
  import { FieldApi, functionalUpdate } from '@tanstack/form-core'
4
4
  import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
@@ -183,5 +183,15 @@ export const Field = (<
183
183
  >): ReactNode => {
184
184
  const fieldApi = useField(fieldOptions as any)
185
185
 
186
- return (<>{functionalUpdate(children, fieldApi as any)}</>) as never
186
+ const jsxToDisplay = useMemo(
187
+ () => functionalUpdate(children, fieldApi as any),
188
+ /**
189
+ * The reason this exists is to fix an issue with the React Compiler.
190
+ * Namely, functionalUpdate is memoized where it checks for `fieldApi`, which is a static type.
191
+ * This means that when `state.value` changes, it does not trigger a re-render. The useMemo explicitly fixes this problem
192
+ */
193
+ // eslint-disable-next-line react-hooks/exhaustive-deps
194
+ [children, fieldApi, fieldApi.state.value, fieldApi.state.meta],
195
+ )
196
+ return (<>{jsxToDisplay}</>) as never
187
197
  }) satisfies FunctionComponent<FieldComponentProps<any, any, any, any, any>>
package/src/useForm.tsx CHANGED
@@ -1,9 +1,9 @@
1
1
  import { FormApi, functionalUpdate } from '@tanstack/form-core'
2
2
  import { useStore } from '@tanstack/react-store'
3
3
  import React, { useState } from 'react'
4
- import { Field, useField } from './useField'
4
+ import { Field } from './useField'
5
5
  import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
6
- import type { ReactNode } from 'react'
6
+ import type { PropsWithChildren, ReactNode } from 'react'
7
7
  import type { FieldComponent, UseField } from './useField'
8
8
  import type { NoInfer } from '@tanstack/react-store'
9
9
  import type { FormOptions, FormState, Validator } from '@tanstack/form-core'
@@ -46,6 +46,19 @@ export type ReactFormExtendedApi<
46
46
  TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
47
47
  > = FormApi<TFormData, TFormValidator> & ReactFormApi<TFormData, TFormValidator>
48
48
 
49
+ function LocalSubscribe({
50
+ form,
51
+ selector,
52
+ children,
53
+ }: PropsWithChildren<{
54
+ form: FormApi<any, any>
55
+ selector: (state: FormState<any>) => FormState<any>
56
+ }>) {
57
+ const data = useStore(form.store, selector)
58
+
59
+ return functionalUpdate(children, data)
60
+ }
61
+
49
62
  /**
50
63
  * A custom React Hook that returns an extended instance of the `FormApi` class.
51
64
  *
@@ -63,17 +76,13 @@ export function useForm<
63
76
  extendedApi.Field = function APIField(props) {
64
77
  return <Field {...props} form={api} />
65
78
  }
66
- // eslint-disable-next-line react-hooks/rules-of-hooks
67
- extendedApi.useField = (props) => useField({ ...props, form: api })
68
- extendedApi.useStore = (selector) => {
69
- // eslint-disable-next-line react-hooks/rules-of-hooks
70
- return useStore(api.store, selector)
71
- }
72
- extendedApi.Subscribe = (props) => {
73
- return functionalUpdate(
74
- props.children,
75
- // eslint-disable-next-line react-hooks/rules-of-hooks
76
- useStore(api.store, props.selector),
79
+ extendedApi.Subscribe = (props: any) => {
80
+ return (
81
+ <LocalSubscribe
82
+ form={api}
83
+ selector={props.selector}
84
+ children={props.children}
85
+ />
77
86
  )
78
87
  }
79
88
 
@@ -82,7 +91,7 @@ export function useForm<
82
91
 
83
92
  useIsomorphicLayoutEffect(formApi.mount, [])
84
93
 
85
- formApi.useStore((state) => state.isSubmitting)
94
+ useStore(formApi.store, (state) => state.isSubmitting)
86
95
 
87
96
  /**
88
97
  * formApi.update should not have any side effects. Think of it like a `useRef`