@tanstack/react-form 0.13.5 → 0.13.7
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.
- package/dist/cjs/createFormFactory.cjs.map +1 -1
- package/dist/cjs/createFormFactory.d.cts +2 -2
- package/dist/cjs/formContext.cjs.map +1 -1
- package/dist/cjs/types.d.cts +1 -1
- package/dist/cjs/useField.cjs +1 -11
- package/dist/cjs/useField.cjs.map +1 -1
- package/dist/cjs/useField.d.cts +1 -1
- package/dist/cjs/useForm.cjs.map +1 -1
- package/dist/cjs/useForm.d.cts +14 -3
- package/dist/esm/createFormFactory.d.ts +2 -2
- package/dist/esm/createFormFactory.js.map +1 -1
- package/dist/esm/formContext.js.map +1 -1
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/useField.d.ts +1 -1
- package/dist/esm/useField.js +2 -12
- package/dist/esm/useField.js.map +1 -1
- package/dist/esm/useForm.d.ts +14 -3
- package/dist/esm/useForm.js.map +1 -1
- package/package.json +5 -16
- package/src/createFormFactory.ts +5 -4
- package/src/formContext.ts +1 -1
- package/src/tests/createFormFactory.test.tsx +1 -1
- package/src/tests/useField.test.tsx +161 -9
- package/src/tests/useForm.test.tsx +25 -1
- package/src/types.ts +1 -1
- package/src/useField.tsx +7 -19
- package/src/useForm.tsx +15 -4
- package/dist/cjs/useIsomorphicEffectOnce.cjs +0 -30
- package/dist/cjs/useIsomorphicEffectOnce.cjs.map +0 -1
- package/dist/cjs/useIsomorphicEffectOnce.d.cts +0 -5
- package/dist/esm/useIsomorphicEffectOnce.d.ts +0 -5
- package/dist/esm/useIsomorphicEffectOnce.js +0 -30
- package/dist/esm/useIsomorphicEffectOnce.js.map +0 -1
- package/src/useIsomorphicEffectOnce.ts +0 -38
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createFormFactory.cjs","sources":["../../src/createFormFactory.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"createFormFactory.cjs","sources":["../../src/createFormFactory.ts"],"sourcesContent":["import { Field, useField } from './useField'\nimport { useForm } from './useForm'\nimport { getValidateFormData } from './validateFormData'\nimport type { ValidateFormData } from './validateFormData'\nimport type { FieldComponent, UseField } from './useField'\nimport type { FormApi, FormOptions, Validator } from '@tanstack/form-core'\n\nexport type FormFactory<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> = {\n useForm: (\n opts?: FormOptions<TFormData, TFormValidator>,\n ) => FormApi<TFormData, TFormValidator>\n useField: UseField<TFormData>\n Field: FieldComponent<TFormData, TFormValidator>\n validateFormData: ValidateFormData<TFormData, TFormValidator>\n initialFormState: Partial<FormApi<TFormData, TFormValidator>['state']>\n}\n\nexport function createFormFactory<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n defaultOpts?: FormOptions<TFormData, TFormValidator>,\n): FormFactory<TFormData, TFormValidator> {\n return {\n useForm: (opts) => {\n const formOptions = Object.assign({}, defaultOpts, opts)\n return useForm<TFormData, TFormValidator>(formOptions)\n },\n useField: useField as any,\n Field: Field as any,\n validateFormData: getValidateFormData(defaultOpts) as never,\n initialFormState: {\n errorMap: {\n onServer: undefined,\n },\n errors: [],\n },\n }\n}\n"],"names":["useForm","useField","Field","getValidateFormData"],"mappings":";;;;;AAoBO,SAAS,kBAId,aACwC;AACjC,SAAA;AAAA,IACL,SAAS,CAAC,SAAS;AACjB,YAAM,cAAc,OAAO,OAAO,CAAA,GAAI,aAAa,IAAI;AACvD,aAAOA,QAAAA,QAAmC,WAAW;AAAA,IACvD;AAAA,IAAA,UACAC,SAAA;AAAA,IAAA,OACAC,SAAA;AAAA,IACA,kBAAkBC,qCAAoB,WAAW;AAAA,IACjD,kBAAkB;AAAA,MAChB,UAAU;AAAA,QACR,UAAU;AAAA,MACZ;AAAA,MACA,QAAQ,CAAC;AAAA,IACX;AAAA,EAAA;AAEJ;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { ValidateFormData } from './validateFormData.cjs';
|
|
2
|
+
import type { FieldComponent, UseField } from './useField.cjs';
|
|
1
3
|
import type { FormApi, FormOptions, Validator } from '@tanstack/form-core';
|
|
2
|
-
import { type UseField, type FieldComponent } from './useField.cjs';
|
|
3
|
-
import { type ValidateFormData } from './validateFormData.cjs';
|
|
4
4
|
export type FormFactory<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> = {
|
|
5
5
|
useForm: (opts?: FormOptions<TFormData, TFormValidator>) => FormApi<TFormData, TFormValidator>;
|
|
6
6
|
useField: UseField<TFormData>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formContext.cjs","sources":["../../src/formContext.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"formContext.cjs","sources":["../../src/formContext.ts"],"sourcesContent":["import { createContext, useContext } from 'rehackt'\nimport type { FormApi, Validator } from '@tanstack/form-core'\n\nexport const formContext = createContext<{\n formApi: FormApi<any, Validator<any, unknown> | undefined>\n parentFieldName?: string\n} | null>(null!)\n\nexport function useFormContext() {\n const formApi = useContext(formContext)\n\n if (!formApi) {\n throw new Error(`You are trying to use the form API outside of a form!`)\n }\n\n return formApi\n}\n"],"names":["createContext","useContext"],"mappings":";;;AAGa,MAAA,cAAcA,sBAGjB,IAAK;AAER,SAAS,iBAAiB;AACzB,QAAA,UAAUC,mBAAW,WAAW;AAEtC,MAAI,CAAC,SAAS;AACN,UAAA,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEO,SAAA;AACT;;;"}
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DeepKeys, DeepValue, FieldOptions, Validator } from '@tanstack/form-core';
|
|
2
2
|
export type UseFieldOptions<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> = FieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData> & {
|
|
3
3
|
mode?: 'value' | 'array';
|
|
4
4
|
};
|
package/dist/cjs/useField.cjs
CHANGED
|
@@ -6,7 +6,6 @@ const reactStore = require("@tanstack/react-store");
|
|
|
6
6
|
const formCore = require("@tanstack/form-core");
|
|
7
7
|
const formContext = require("./formContext.cjs");
|
|
8
8
|
const useIsomorphicLayoutEffect = require("./useIsomorphicLayoutEffect.cjs");
|
|
9
|
-
const useIsomorphicEffectOnce = require("./useIsomorphicEffectOnce.cjs");
|
|
10
9
|
function useField(opts) {
|
|
11
10
|
const { formApi, parentFieldName } = formContext.useFormContext();
|
|
12
11
|
const [fieldApi] = rehackt.useState(() => {
|
|
@@ -20,6 +19,7 @@ function useField(opts) {
|
|
|
20
19
|
api.Field = Field;
|
|
21
20
|
return api;
|
|
22
21
|
});
|
|
22
|
+
useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi]);
|
|
23
23
|
useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
|
|
24
24
|
fieldApi.update({ ...opts, form: formApi });
|
|
25
25
|
});
|
|
@@ -29,16 +29,6 @@ function useField(opts) {
|
|
|
29
29
|
return [state.meta, Object.keys(state.value).length];
|
|
30
30
|
} : void 0
|
|
31
31
|
);
|
|
32
|
-
const unmountFn = rehackt.useRef(null);
|
|
33
|
-
useIsomorphicEffectOnce.useIsomorphicEffectOnce(() => {
|
|
34
|
-
return () => {
|
|
35
|
-
var _a;
|
|
36
|
-
(_a = unmountFn.current) == null ? void 0 : _a.call(unmountFn);
|
|
37
|
-
};
|
|
38
|
-
});
|
|
39
|
-
if (!unmountFn.current) {
|
|
40
|
-
unmountFn.current = fieldApi.mount();
|
|
41
|
-
}
|
|
42
32
|
return fieldApi;
|
|
43
33
|
}
|
|
44
34
|
function Field({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useField.cjs","sources":["../../src/useField.tsx"],"sourcesContent":["import React, {
|
|
1
|
+
{"version":3,"file":"useField.cjs","sources":["../../src/useField.tsx"],"sourcesContent":["import React, { useState } from 'rehackt'\nimport { useStore } from '@tanstack/react-store'\nimport { FieldApi, functionalUpdate } from '@tanstack/form-core'\nimport { formContext, useFormContext } from './formContext'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { UseFieldOptions } from './types'\nimport type {\n DeepKeys,\n DeepValue,\n Narrow,\n Validator,\n} from '@tanstack/form-core'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FieldApi<\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 Field: FieldComponent<TParentData, TFormValidator>\n }\n}\n\nexport type UseField<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>(\n opts?: { name: Narrow<TName> } & UseFieldOptions<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator\n >,\n) => FieldApi<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n DeepValue<TParentData, TName>\n>\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>(\n opts: UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>,\n): FieldApi<TParentData, TName, TFieldValidator, TFormValidator> {\n // Get the form API either manually or from context\n const { formApi, parentFieldName } = useFormContext()\n\n const [fieldApi] = useState(() => {\n const name = (\n typeof opts.index === 'number'\n ? [parentFieldName, opts.index, opts.name]\n : [parentFieldName, opts.name]\n )\n .filter((d) => d !== undefined)\n .join('.')\n\n const api = new FieldApi({\n ...opts,\n form: formApi as never,\n // TODO: Fix typings to include `index` and `parentFieldName`, if present\n name: name as typeof opts.name as never,\n })\n\n api.Field = Field as never\n\n return api\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, form: formApi } as never)\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 as never\n}\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 ) => any\n} & (TParentData extends any[]\n ? {\n name?: TName\n index: number\n }\n : {\n name: TName\n index?: never\n }) &\n Omit<\n UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>,\n 'name' | 'index'\n >\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}: FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n>) => any\n\nexport function 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>({\n children,\n ...fieldOptions\n}: {\n children: (\n fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator>,\n ) => any\n} & UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>) {\n const fieldApi = useField(fieldOptions as any)\n\n return (\n <formContext.Provider\n value={{\n formApi: fieldApi.form as never,\n parentFieldName: fieldApi.name,\n }}\n children={functionalUpdate(children, fieldApi as any)}\n />\n )\n}\n"],"names":["useFormContext","useState","FieldApi","useIsomorphicLayoutEffect","useStore","jsx","formContext","functionalUpdate"],"mappings":";;;;;;;;AAqDO,SAAS,SAUd,MAC+D;AAE/D,QAAM,EAAE,SAAS,gBAAgB,IAAIA,YAAe,eAAA;AAEpD,QAAM,CAAC,QAAQ,IAAIC,QAAAA,SAAS,MAAM;AAC1B,UAAA,QACJ,OAAO,KAAK,UAAU,WAClB,CAAC,iBAAiB,KAAK,OAAO,KAAK,IAAI,IACvC,CAAC,iBAAiB,KAAK,IAAI,GAE9B,OAAO,CAAC,MAAM,MAAM,MAAS,EAC7B,KAAK,GAAG;AAEL,UAAA,MAAM,IAAIC,kBAAS;AAAA,MACvB,GAAG;AAAA,MACH,MAAM;AAAA;AAAA,MAEN;AAAA,IAAA,CACD;AAED,QAAI,QAAQ;AAEL,WAAA;AAAA,EAAA,CACR;AAEDC,4BAAAA,0BAA0B,SAAS,OAAO,CAAC,QAAQ,CAAC;AAMpDA,4BAAAA,0BAA0B,MAAM;AAC9B,aAAS,OAAO,EAAE,GAAG,MAAM,MAAM,SAAkB;AAAA,EAAA,CACpD;AAEDC,aAAA;AAAA,IACE,SAAS;AAAA,IACT,KAAK,SAAS,UACV,CAAC,UAAU;AACF,aAAA,CAAC,MAAM,MAAM,OAAO,KAAK,MAAM,KAAK,EAAE,MAAM;AAAA,IAErD,IAAA;AAAA,EAAA;AAGC,SAAA;AACT;AA0DO,SAAS,MASd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAI0E;AAClE,QAAA,WAAW,SAAS,YAAmB;AAG3C,SAAAC,2BAAA;AAAA,IAACC,YAAAA,YAAY;AAAA,IAAZ;AAAA,MACC,OAAO;AAAA,QACL,SAAS,SAAS;AAAA,QAClB,iBAAiB,SAAS;AAAA,MAC5B;AAAA,MACA,UAAUC,SAAAA,iBAAiB,UAAU,QAAe;AAAA,IAAA;AAAA,EAAA;AAG1D;;;"}
|
package/dist/cjs/useField.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import React from 'rehackt';
|
|
3
|
-
import type { DeepKeys, DeepValue, Narrow, Validator } from '@tanstack/form-core';
|
|
4
3
|
import { FieldApi } from '@tanstack/form-core';
|
|
5
4
|
import type { UseFieldOptions } from './types.cjs';
|
|
5
|
+
import type { DeepKeys, DeepValue, Narrow, Validator } from '@tanstack/form-core';
|
|
6
6
|
declare module '@tanstack/form-core' {
|
|
7
7
|
interface FieldApi<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> {
|
|
8
8
|
Field: FieldComponent<TParentData, TFormValidator>;
|
package/dist/cjs/useForm.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useForm.cjs","sources":["../../src/useForm.tsx"],"sourcesContent":["import
|
|
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, {\n type PropsWithChildren,\n type ReactNode,\n useState,\n} from 'rehackt'\nimport { Field, useField } from './useField'\nimport { formContext } from './formContext'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { NoInfer } from '@tanstack/react-store'\nimport type { FormOptions, FormState, Validator } from '@tanstack/form-core'\nimport type { FieldComponent, UseField } from './useField'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FormApi<TFormData, TFormValidator> {\n Provider: (props: PropsWithChildren) => JSX.Element\n Field: FieldComponent<TFormData, TFormValidator>\n useField: UseField<TFormData>\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\n Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {\n /**\n TypeScript versions <=5.0.4 have a bug that prevents\n the type of the `TSelected` generic from being inferred\n from the return type of this method.\n\n In these versions, `TSelected` will fall back to the default\n type (or `unknown` if that's not defined).\n\n @see {@link https://github.com/TanStack/form/pull/606/files#r1506715714 | This discussion on GitHub for the details}\n @see {@link https://github.com/microsoft/TypeScript/issues/52786 | The bug report in `microsoft/TypeScript`}\n */\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => JSX.Element\n }\n}\n\nexport function useForm<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n opts?: FormOptions<TFormData, TFormValidator>,\n): FormApi<TFormData, TFormValidator> {\n const [formApi] = useState(() => {\n const api = new FormApi<TFormData, TFormValidator>(opts)\n\n api.Provider = function Provider(props) {\n useIsomorphicLayoutEffect(api.mount, [])\n return (\n <formContext.Provider {...props} value={{ formApi: api as never }} />\n )\n }\n api.Field = Field as any\n api.useField = useField as any\n api.useStore = (\n // @ts-ignore\n selector,\n ) => {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useStore(api.store as any, selector as any) as any\n }\n api.Subscribe = (\n // @ts-ignore\n props,\n ) => {\n return functionalUpdate(\n props.children,\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useStore(api.store as any, props.selector as any),\n ) as any\n }\n\n return api\n })\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 as any\n}\n"],"names":["useState","FormApi","useIsomorphicLayoutEffect","jsx","formContext","Field","useField","useStore","functionalUpdate"],"mappings":";;;;;;;;;AAyCO,SAAS,QAId,MACoC;AACpC,QAAM,CAAC,OAAO,IAAIA,QAAAA,SAAS,MAAM;AACzB,UAAA,MAAM,IAAIC,iBAAmC,IAAI;AAEnD,QAAA,WAAW,SAAS,SAAS,OAAO;AACZC,gCAAAA,0BAAA,IAAI,OAAO,CAAA,CAAE;AAErC,aAAAC,+BAACC,YAAAA,YAAY,UAAZ,EAAsB,GAAG,OAAO,OAAO,EAAE,SAAS,IAAgB,EAAA,CAAA;AAAA,IAAA;AAGvE,QAAI,QAAQC;AACZ,QAAI,WAAWC;AACX,QAAA,WAAW,CAEb,aACG;AAEI,aAAAC,oBAAS,IAAI,OAAc,QAAe;AAAA,IAAA;AAE/C,QAAA,YAAY,CAEd,UACG;AACI,aAAAC,SAAA;AAAA,QACL,MAAM;AAAA;AAAA,QAEND,WAAAA,SAAS,IAAI,OAAc,MAAM,QAAe;AAAA,MAAA;AAAA,IAClD;AAGK,WAAA;AAAA,EAAA,CACR;AAED,UAAQ,SAAS,CAAC,UAAU,MAAM,YAAY;AAM9CL,4BAAAA,0BAA0B,MAAM;AAC9B,YAAQ,OAAO,IAAI;AAAA,EAAA,CACpB;AAEM,SAAA;AACT;;"}
|
package/dist/cjs/useForm.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { FormState, FormOptions, Validator } from '@tanstack/form-core';
|
|
3
2
|
import { FormApi } from '@tanstack/form-core';
|
|
4
|
-
import type { NoInfer } from '@tanstack/react-store';
|
|
5
3
|
import { type PropsWithChildren, type ReactNode } from 'rehackt';
|
|
6
|
-
import
|
|
4
|
+
import type { NoInfer } from '@tanstack/react-store';
|
|
5
|
+
import type { FormOptions, FormState, Validator } from '@tanstack/form-core';
|
|
6
|
+
import type { FieldComponent, UseField } from './useField.cjs';
|
|
7
7
|
declare module '@tanstack/form-core' {
|
|
8
8
|
interface FormApi<TFormData, TFormValidator> {
|
|
9
9
|
Provider: (props: PropsWithChildren) => JSX.Element;
|
|
@@ -11,6 +11,17 @@ declare module '@tanstack/form-core' {
|
|
|
11
11
|
useField: UseField<TFormData>;
|
|
12
12
|
useStore: <TSelected = NoInfer<FormState<TFormData>>>(selector?: (state: NoInfer<FormState<TFormData>>) => TSelected) => TSelected;
|
|
13
13
|
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
|
|
14
|
+
/**
|
|
15
|
+
TypeScript versions <=5.0.4 have a bug that prevents
|
|
16
|
+
the type of the `TSelected` generic from being inferred
|
|
17
|
+
from the return type of this method.
|
|
18
|
+
|
|
19
|
+
In these versions, `TSelected` will fall back to the default
|
|
20
|
+
type (or `unknown` if that's not defined).
|
|
21
|
+
|
|
22
|
+
@see {@link https://github.com/TanStack/form/pull/606/files#r1506715714 | This discussion on GitHub for the details}
|
|
23
|
+
@see {@link https://github.com/microsoft/TypeScript/issues/52786 | The bug report in `microsoft/TypeScript`}
|
|
24
|
+
*/
|
|
14
25
|
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected;
|
|
15
26
|
children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode;
|
|
16
27
|
}) => JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { ValidateFormData } from './validateFormData.js';
|
|
2
|
+
import type { FieldComponent, UseField } from './useField.js';
|
|
1
3
|
import type { FormApi, FormOptions, Validator } from '@tanstack/form-core';
|
|
2
|
-
import { type UseField, type FieldComponent } from './useField.js';
|
|
3
|
-
import { type ValidateFormData } from './validateFormData.js';
|
|
4
4
|
export type FormFactory<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> = {
|
|
5
5
|
useForm: (opts?: FormOptions<TFormData, TFormValidator>) => FormApi<TFormData, TFormValidator>;
|
|
6
6
|
useField: UseField<TFormData>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createFormFactory.js","sources":["../../src/createFormFactory.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"createFormFactory.js","sources":["../../src/createFormFactory.ts"],"sourcesContent":["import { Field, useField } from './useField'\nimport { useForm } from './useForm'\nimport { getValidateFormData } from './validateFormData'\nimport type { ValidateFormData } from './validateFormData'\nimport type { FieldComponent, UseField } from './useField'\nimport type { FormApi, FormOptions, Validator } from '@tanstack/form-core'\n\nexport type FormFactory<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n> = {\n useForm: (\n opts?: FormOptions<TFormData, TFormValidator>,\n ) => FormApi<TFormData, TFormValidator>\n useField: UseField<TFormData>\n Field: FieldComponent<TFormData, TFormValidator>\n validateFormData: ValidateFormData<TFormData, TFormValidator>\n initialFormState: Partial<FormApi<TFormData, TFormValidator>['state']>\n}\n\nexport function createFormFactory<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n defaultOpts?: FormOptions<TFormData, TFormValidator>,\n): FormFactory<TFormData, TFormValidator> {\n return {\n useForm: (opts) => {\n const formOptions = Object.assign({}, defaultOpts, opts)\n return useForm<TFormData, TFormValidator>(formOptions)\n },\n useField: useField as any,\n Field: Field as any,\n validateFormData: getValidateFormData(defaultOpts) as never,\n initialFormState: {\n errorMap: {\n onServer: undefined,\n },\n errors: [],\n },\n }\n}\n"],"names":[],"mappings":";;;AAoBO,SAAS,kBAId,aACwC;AACjC,SAAA;AAAA,IACL,SAAS,CAAC,SAAS;AACjB,YAAM,cAAc,OAAO,OAAO,CAAA,GAAI,aAAa,IAAI;AACvD,aAAO,QAAmC,WAAW;AAAA,IACvD;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB,oBAAoB,WAAW;AAAA,IACjD,kBAAkB;AAAA,MAChB,UAAU;AAAA,QACR,UAAU;AAAA,MACZ;AAAA,MACA,QAAQ,CAAC;AAAA,IACX;AAAA,EAAA;AAEJ;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formContext.js","sources":["../../src/formContext.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"formContext.js","sources":["../../src/formContext.ts"],"sourcesContent":["import { createContext, useContext } from 'rehackt'\nimport type { FormApi, Validator } from '@tanstack/form-core'\n\nexport const formContext = createContext<{\n formApi: FormApi<any, Validator<any, unknown> | undefined>\n parentFieldName?: string\n} | null>(null!)\n\nexport function useFormContext() {\n const formApi = useContext(formContext)\n\n if (!formApi) {\n throw new Error(`You are trying to use the form API outside of a form!`)\n }\n\n return formApi\n}\n"],"names":[],"mappings":";AAGa,MAAA,cAAc,cAGjB,IAAK;AAER,SAAS,iBAAiB;AACzB,QAAA,UAAU,WAAW,WAAW;AAEtC,MAAI,CAAC,SAAS;AACN,UAAA,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEO,SAAA;AACT;"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DeepKeys, DeepValue, FieldOptions, Validator } from '@tanstack/form-core';
|
|
2
2
|
export type UseFieldOptions<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> = FieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData> & {
|
|
3
3
|
mode?: 'value' | 'array';
|
|
4
4
|
};
|
package/dist/esm/useField.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import React from 'rehackt';
|
|
3
|
-
import type { DeepKeys, DeepValue, Narrow, Validator } from '@tanstack/form-core';
|
|
4
3
|
import { FieldApi } from '@tanstack/form-core';
|
|
5
4
|
import type { UseFieldOptions } from './types.js';
|
|
5
|
+
import type { DeepKeys, DeepValue, Narrow, Validator } from '@tanstack/form-core';
|
|
6
6
|
declare module '@tanstack/form-core' {
|
|
7
7
|
interface FieldApi<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> {
|
|
8
8
|
Field: FieldComponent<TParentData, TFormValidator>;
|
package/dist/esm/useField.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState
|
|
2
|
+
import { useState } from "rehackt";
|
|
3
3
|
import { useStore } from "@tanstack/react-store";
|
|
4
4
|
import { FieldApi, functionalUpdate } from "@tanstack/form-core";
|
|
5
5
|
import { useFormContext, formContext } from "./formContext.js";
|
|
6
6
|
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
|
|
7
|
-
import { useIsomorphicEffectOnce } from "./useIsomorphicEffectOnce.js";
|
|
8
7
|
function useField(opts) {
|
|
9
8
|
const { formApi, parentFieldName } = useFormContext();
|
|
10
9
|
const [fieldApi] = useState(() => {
|
|
@@ -18,6 +17,7 @@ function useField(opts) {
|
|
|
18
17
|
api.Field = Field;
|
|
19
18
|
return api;
|
|
20
19
|
});
|
|
20
|
+
useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi]);
|
|
21
21
|
useIsomorphicLayoutEffect(() => {
|
|
22
22
|
fieldApi.update({ ...opts, form: formApi });
|
|
23
23
|
});
|
|
@@ -27,16 +27,6 @@ function useField(opts) {
|
|
|
27
27
|
return [state.meta, Object.keys(state.value).length];
|
|
28
28
|
} : void 0
|
|
29
29
|
);
|
|
30
|
-
const unmountFn = useRef(null);
|
|
31
|
-
useIsomorphicEffectOnce(() => {
|
|
32
|
-
return () => {
|
|
33
|
-
var _a;
|
|
34
|
-
(_a = unmountFn.current) == null ? void 0 : _a.call(unmountFn);
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
if (!unmountFn.current) {
|
|
38
|
-
unmountFn.current = fieldApi.mount();
|
|
39
|
-
}
|
|
40
30
|
return fieldApi;
|
|
41
31
|
}
|
|
42
32
|
function Field({
|
package/dist/esm/useField.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useField.js","sources":["../../src/useField.tsx"],"sourcesContent":["import React, {
|
|
1
|
+
{"version":3,"file":"useField.js","sources":["../../src/useField.tsx"],"sourcesContent":["import React, { useState } from 'rehackt'\nimport { useStore } from '@tanstack/react-store'\nimport { FieldApi, functionalUpdate } from '@tanstack/form-core'\nimport { formContext, useFormContext } from './formContext'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { UseFieldOptions } from './types'\nimport type {\n DeepKeys,\n DeepValue,\n Narrow,\n Validator,\n} from '@tanstack/form-core'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FieldApi<\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 Field: FieldComponent<TParentData, TFormValidator>\n }\n}\n\nexport type UseField<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>(\n opts?: { name: Narrow<TName> } & UseFieldOptions<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator\n >,\n) => FieldApi<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n DeepValue<TParentData, TName>\n>\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>(\n opts: UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>,\n): FieldApi<TParentData, TName, TFieldValidator, TFormValidator> {\n // Get the form API either manually or from context\n const { formApi, parentFieldName } = useFormContext()\n\n const [fieldApi] = useState(() => {\n const name = (\n typeof opts.index === 'number'\n ? [parentFieldName, opts.index, opts.name]\n : [parentFieldName, opts.name]\n )\n .filter((d) => d !== undefined)\n .join('.')\n\n const api = new FieldApi({\n ...opts,\n form: formApi as never,\n // TODO: Fix typings to include `index` and `parentFieldName`, if present\n name: name as typeof opts.name as never,\n })\n\n api.Field = Field as never\n\n return api\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, form: formApi } as never)\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 as never\n}\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 ) => any\n} & (TParentData extends any[]\n ? {\n name?: TName\n index: number\n }\n : {\n name: TName\n index?: never\n }) &\n Omit<\n UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>,\n 'name' | 'index'\n >\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}: FieldComponentProps<\n TParentData,\n TName,\n TFieldValidator,\n TFormValidator,\n TData\n>) => any\n\nexport function 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>({\n children,\n ...fieldOptions\n}: {\n children: (\n fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator>,\n ) => any\n} & UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>) {\n const fieldApi = useField(fieldOptions as any)\n\n return (\n <formContext.Provider\n value={{\n formApi: fieldApi.form as never,\n parentFieldName: fieldApi.name,\n }}\n children={functionalUpdate(children, fieldApi as any)}\n />\n )\n}\n"],"names":[],"mappings":";;;;;;AAqDO,SAAS,SAUd,MAC+D;AAE/D,QAAM,EAAE,SAAS,gBAAgB,IAAI,eAAe;AAEpD,QAAM,CAAC,QAAQ,IAAI,SAAS,MAAM;AAC1B,UAAA,QACJ,OAAO,KAAK,UAAU,WAClB,CAAC,iBAAiB,KAAK,OAAO,KAAK,IAAI,IACvC,CAAC,iBAAiB,KAAK,IAAI,GAE9B,OAAO,CAAC,MAAM,MAAM,MAAS,EAC7B,KAAK,GAAG;AAEL,UAAA,MAAM,IAAI,SAAS;AAAA,MACvB,GAAG;AAAA,MACH,MAAM;AAAA;AAAA,MAEN;AAAA,IAAA,CACD;AAED,QAAI,QAAQ;AAEL,WAAA;AAAA,EAAA,CACR;AAED,4BAA0B,SAAS,OAAO,CAAC,QAAQ,CAAC;AAMpD,4BAA0B,MAAM;AAC9B,aAAS,OAAO,EAAE,GAAG,MAAM,MAAM,SAAkB;AAAA,EAAA,CACpD;AAED;AAAA,IACE,SAAS;AAAA,IACT,KAAK,SAAS,UACV,CAAC,UAAU;AACF,aAAA,CAAC,MAAM,MAAM,OAAO,KAAK,MAAM,KAAK,EAAE,MAAM;AAAA,IAErD,IAAA;AAAA,EAAA;AAGC,SAAA;AACT;AA0DO,SAAS,MASd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAI0E;AAClE,QAAA,WAAW,SAAS,YAAmB;AAG3C,SAAA;AAAA,IAAC,YAAY;AAAA,IAAZ;AAAA,MACC,OAAO;AAAA,QACL,SAAS,SAAS;AAAA,QAClB,iBAAiB,SAAS;AAAA,MAC5B;AAAA,MACA,UAAU,iBAAiB,UAAU,QAAe;AAAA,IAAA;AAAA,EAAA;AAG1D;"}
|
package/dist/esm/useForm.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { FormState, FormOptions, Validator } from '@tanstack/form-core';
|
|
3
2
|
import { FormApi } from '@tanstack/form-core';
|
|
4
|
-
import type { NoInfer } from '@tanstack/react-store';
|
|
5
3
|
import { type PropsWithChildren, type ReactNode } from 'rehackt';
|
|
6
|
-
import
|
|
4
|
+
import type { NoInfer } from '@tanstack/react-store';
|
|
5
|
+
import type { FormOptions, FormState, Validator } from '@tanstack/form-core';
|
|
6
|
+
import type { FieldComponent, UseField } from './useField.js';
|
|
7
7
|
declare module '@tanstack/form-core' {
|
|
8
8
|
interface FormApi<TFormData, TFormValidator> {
|
|
9
9
|
Provider: (props: PropsWithChildren) => JSX.Element;
|
|
@@ -11,6 +11,17 @@ declare module '@tanstack/form-core' {
|
|
|
11
11
|
useField: UseField<TFormData>;
|
|
12
12
|
useStore: <TSelected = NoInfer<FormState<TFormData>>>(selector?: (state: NoInfer<FormState<TFormData>>) => TSelected) => TSelected;
|
|
13
13
|
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
|
|
14
|
+
/**
|
|
15
|
+
TypeScript versions <=5.0.4 have a bug that prevents
|
|
16
|
+
the type of the `TSelected` generic from being inferred
|
|
17
|
+
from the return type of this method.
|
|
18
|
+
|
|
19
|
+
In these versions, `TSelected` will fall back to the default
|
|
20
|
+
type (or `unknown` if that's not defined).
|
|
21
|
+
|
|
22
|
+
@see {@link https://github.com/TanStack/form/pull/606/files#r1506715714 | This discussion on GitHub for the details}
|
|
23
|
+
@see {@link https://github.com/microsoft/TypeScript/issues/52786 | The bug report in `microsoft/TypeScript`}
|
|
24
|
+
*/
|
|
14
25
|
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected;
|
|
15
26
|
children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode;
|
|
16
27
|
}) => JSX.Element;
|
package/dist/esm/useForm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useForm.js","sources":["../../src/useForm.tsx"],"sourcesContent":["import
|
|
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, {\n type PropsWithChildren,\n type ReactNode,\n useState,\n} from 'rehackt'\nimport { Field, useField } from './useField'\nimport { formContext } from './formContext'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { NoInfer } from '@tanstack/react-store'\nimport type { FormOptions, FormState, Validator } from '@tanstack/form-core'\nimport type { FieldComponent, UseField } from './useField'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FormApi<TFormData, TFormValidator> {\n Provider: (props: PropsWithChildren) => JSX.Element\n Field: FieldComponent<TFormData, TFormValidator>\n useField: UseField<TFormData>\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\n Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {\n /**\n TypeScript versions <=5.0.4 have a bug that prevents\n the type of the `TSelected` generic from being inferred\n from the return type of this method.\n\n In these versions, `TSelected` will fall back to the default\n type (or `unknown` if that's not defined).\n\n @see {@link https://github.com/TanStack/form/pull/606/files#r1506715714 | This discussion on GitHub for the details}\n @see {@link https://github.com/microsoft/TypeScript/issues/52786 | The bug report in `microsoft/TypeScript`}\n */\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode\n }) => JSX.Element\n }\n}\n\nexport function useForm<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n opts?: FormOptions<TFormData, TFormValidator>,\n): FormApi<TFormData, TFormValidator> {\n const [formApi] = useState(() => {\n const api = new FormApi<TFormData, TFormValidator>(opts)\n\n api.Provider = function Provider(props) {\n useIsomorphicLayoutEffect(api.mount, [])\n return (\n <formContext.Provider {...props} value={{ formApi: api as never }} />\n )\n }\n api.Field = Field as any\n api.useField = useField as any\n api.useStore = (\n // @ts-ignore\n selector,\n ) => {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useStore(api.store as any, selector as any) as any\n }\n api.Subscribe = (\n // @ts-ignore\n props,\n ) => {\n return functionalUpdate(\n props.children,\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useStore(api.store as any, props.selector as any),\n ) as any\n }\n\n return api\n })\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 as any\n}\n"],"names":[],"mappings":";;;;;;;AAyCO,SAAS,QAId,MACoC;AACpC,QAAM,CAAC,OAAO,IAAI,SAAS,MAAM;AACzB,UAAA,MAAM,IAAI,QAAmC,IAAI;AAEnD,QAAA,WAAW,SAAS,SAAS,OAAO;AACZ,gCAAA,IAAI,OAAO,CAAA,CAAE;AAErC,aAAA,oBAAC,YAAY,UAAZ,EAAsB,GAAG,OAAO,OAAO,EAAE,SAAS,IAAgB,EAAA,CAAA;AAAA,IAAA;AAGvE,QAAI,QAAQ;AACZ,QAAI,WAAW;AACX,QAAA,WAAW,CAEb,aACG;AAEI,aAAA,SAAS,IAAI,OAAc,QAAe;AAAA,IAAA;AAE/C,QAAA,YAAY,CAEd,UACG;AACI,aAAA;AAAA,QACL,MAAM;AAAA;AAAA,QAEN,SAAS,IAAI,OAAc,MAAM,QAAe;AAAA,MAAA;AAAA,IAClD;AAGK,WAAA;AAAA,EAAA,CACR;AAED,UAAQ,SAAS,CAAC,UAAU,MAAM,YAAY;AAM9C,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.13.
|
|
3
|
+
"version": "0.13.7",
|
|
4
4
|
"description": "Powerful, type-safe forms for React.",
|
|
5
5
|
"author": "tannerlinsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,8 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/react": "^18.2.45",
|
|
37
|
-
"@types/react-dom": "^18.
|
|
38
|
-
"@types/use-sync-external-store": "^0.0.3",
|
|
37
|
+
"@types/react-dom": "^18.2.19",
|
|
39
38
|
"@vitejs/plugin-react": "^4.2.1",
|
|
40
39
|
"react": "^18.2.0",
|
|
41
40
|
"react-dom": "^18.2.0"
|
|
@@ -44,26 +43,16 @@
|
|
|
44
43
|
"@tanstack/react-store": "^0.3.1",
|
|
45
44
|
"decode-formdata": "^0.4.0",
|
|
46
45
|
"rehackt": "^0.0.3",
|
|
47
|
-
"@tanstack/form-core": "0.13.
|
|
46
|
+
"@tanstack/form-core": "0.13.7"
|
|
48
47
|
},
|
|
49
48
|
"peerDependencies": {
|
|
50
|
-
"react": "^17.0.0 || ^18.0.0"
|
|
51
|
-
"react-dom": "^17.0.0 || ^18.0.0",
|
|
52
|
-
"react-native": "*"
|
|
53
|
-
},
|
|
54
|
-
"peerDependenciesMeta": {
|
|
55
|
-
"react-dom": {
|
|
56
|
-
"optional": true
|
|
57
|
-
},
|
|
58
|
-
"react-native": {
|
|
59
|
-
"optional": true
|
|
60
|
-
}
|
|
49
|
+
"react": "^17.0.0 || ^18.0.0"
|
|
61
50
|
},
|
|
62
51
|
"scripts": {
|
|
63
52
|
"clean": "rimraf ./dist && rimraf ./coverage",
|
|
64
53
|
"test:eslint": "eslint --ext .ts,.tsx ./src",
|
|
65
54
|
"test:types:versions49": "node ../../node_modules/typescript49/lib/tsc.js --project tsconfig.legacy.json",
|
|
66
|
-
"test:types:versions50": "node ../../node_modules/typescript50/lib/tsc.js",
|
|
55
|
+
"test:types:versions50": "node ../../node_modules/typescript50/lib/tsc.js --project tsconfig.50.json",
|
|
67
56
|
"test:types:versions51": "node ../../node_modules/typescript51/lib/tsc.js",
|
|
68
57
|
"test:types:versions52": "tsc",
|
|
69
58
|
"test:types": "pnpm run \"/^test:types:versions.*/\"",
|
package/src/createFormFactory.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { type UseField, type FieldComponent, Field, useField } from './useField'
|
|
1
|
+
import { Field, useField } from './useField'
|
|
4
2
|
import { useForm } from './useForm'
|
|
5
|
-
import {
|
|
3
|
+
import { getValidateFormData } from './validateFormData'
|
|
4
|
+
import type { ValidateFormData } from './validateFormData'
|
|
5
|
+
import type { FieldComponent, UseField } from './useField'
|
|
6
|
+
import type { FormApi, FormOptions, Validator } from '@tanstack/form-core'
|
|
6
7
|
|
|
7
8
|
export type FormFactory<
|
|
8
9
|
TFormData,
|
package/src/formContext.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { FormApi, Validator } from '@tanstack/form-core'
|
|
2
1
|
import { createContext, useContext } from 'rehackt'
|
|
2
|
+
import type { FormApi, Validator } from '@tanstack/form-core'
|
|
3
3
|
|
|
4
4
|
export const formContext = createContext<{
|
|
5
5
|
formApi: FormApi<any, Validator<any, unknown> | undefined>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { render } from '@testing-library/react'
|
|
3
3
|
import '@testing-library/jest-dom'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
-
import { createFormFactory } from '
|
|
5
|
+
import { createFormFactory } from '../index'
|
|
6
6
|
|
|
7
7
|
describe('createFormFactory', () => {
|
|
8
8
|
it('should allow default values to be set', async () => {
|
|
@@ -3,8 +3,9 @@ import * as React from 'react'
|
|
|
3
3
|
import { render, waitFor } from '@testing-library/react'
|
|
4
4
|
import userEvent from '@testing-library/user-event'
|
|
5
5
|
import '@testing-library/jest-dom'
|
|
6
|
-
import {
|
|
6
|
+
import { createFormFactory, useForm } from '../index'
|
|
7
7
|
import { sleep } from './utils'
|
|
8
|
+
import type { FieldApi, FormApi } from '../index'
|
|
8
9
|
|
|
9
10
|
const user = userEvent.setup()
|
|
10
11
|
|
|
@@ -100,7 +101,12 @@ describe('useField', () => {
|
|
|
100
101
|
const formFactory = createFormFactory<Person>()
|
|
101
102
|
|
|
102
103
|
function Comp() {
|
|
103
|
-
const form = formFactory.useForm(
|
|
104
|
+
const form = formFactory.useForm({
|
|
105
|
+
defaultValues: {
|
|
106
|
+
firstName: '',
|
|
107
|
+
lastName: '',
|
|
108
|
+
},
|
|
109
|
+
})
|
|
104
110
|
|
|
105
111
|
return (
|
|
106
112
|
<form.Provider>
|
|
@@ -142,7 +148,12 @@ describe('useField', () => {
|
|
|
142
148
|
const formFactory = createFormFactory<Person>()
|
|
143
149
|
|
|
144
150
|
function Comp() {
|
|
145
|
-
const form = formFactory.useForm(
|
|
151
|
+
const form = formFactory.useForm({
|
|
152
|
+
defaultValues: {
|
|
153
|
+
firstName: '',
|
|
154
|
+
lastName: '',
|
|
155
|
+
},
|
|
156
|
+
})
|
|
146
157
|
|
|
147
158
|
return (
|
|
148
159
|
<form.Provider>
|
|
@@ -187,7 +198,12 @@ describe('useField', () => {
|
|
|
187
198
|
const formFactory = createFormFactory<Person>()
|
|
188
199
|
|
|
189
200
|
function Comp() {
|
|
190
|
-
const form = formFactory.useForm(
|
|
201
|
+
const form = formFactory.useForm({
|
|
202
|
+
defaultValues: {
|
|
203
|
+
firstName: '',
|
|
204
|
+
lastName: '',
|
|
205
|
+
},
|
|
206
|
+
})
|
|
191
207
|
|
|
192
208
|
return (
|
|
193
209
|
<form.Provider>
|
|
@@ -238,7 +254,12 @@ describe('useField', () => {
|
|
|
238
254
|
const formFactory = createFormFactory<Person>()
|
|
239
255
|
|
|
240
256
|
function Comp() {
|
|
241
|
-
const form = formFactory.useForm(
|
|
257
|
+
const form = formFactory.useForm({
|
|
258
|
+
defaultValues: {
|
|
259
|
+
firstName: '',
|
|
260
|
+
lastName: '',
|
|
261
|
+
},
|
|
262
|
+
})
|
|
242
263
|
|
|
243
264
|
return (
|
|
244
265
|
<form.Provider>
|
|
@@ -287,7 +308,12 @@ describe('useField', () => {
|
|
|
287
308
|
const formFactory = createFormFactory<Person>()
|
|
288
309
|
|
|
289
310
|
function Comp() {
|
|
290
|
-
const form = formFactory.useForm(
|
|
311
|
+
const form = formFactory.useForm({
|
|
312
|
+
defaultValues: {
|
|
313
|
+
firstName: '',
|
|
314
|
+
lastName: '',
|
|
315
|
+
},
|
|
316
|
+
})
|
|
291
317
|
|
|
292
318
|
return (
|
|
293
319
|
<form.Provider>
|
|
@@ -345,7 +371,12 @@ describe('useField', () => {
|
|
|
345
371
|
const formFactory = createFormFactory<Person>()
|
|
346
372
|
|
|
347
373
|
function Comp() {
|
|
348
|
-
const form = formFactory.useForm(
|
|
374
|
+
const form = formFactory.useForm({
|
|
375
|
+
defaultValues: {
|
|
376
|
+
firstName: '',
|
|
377
|
+
lastName: '',
|
|
378
|
+
},
|
|
379
|
+
})
|
|
349
380
|
|
|
350
381
|
return (
|
|
351
382
|
<form.Provider>
|
|
@@ -394,7 +425,12 @@ describe('useField', () => {
|
|
|
394
425
|
const formFactory = createFormFactory<Person>()
|
|
395
426
|
let form: FormApi<Person> | null = null
|
|
396
427
|
function Comp() {
|
|
397
|
-
form = formFactory.useForm(
|
|
428
|
+
form = formFactory.useForm({
|
|
429
|
+
defaultValues: {
|
|
430
|
+
firstName: '',
|
|
431
|
+
lastName: '',
|
|
432
|
+
},
|
|
433
|
+
})
|
|
398
434
|
return (
|
|
399
435
|
<form.Provider>
|
|
400
436
|
<form.Field
|
|
@@ -432,7 +468,12 @@ describe('useField', () => {
|
|
|
432
468
|
const formFactory = createFormFactory<Person>()
|
|
433
469
|
let form: FormApi<Person> | null = null
|
|
434
470
|
function Comp() {
|
|
435
|
-
form = formFactory.useForm(
|
|
471
|
+
form = formFactory.useForm({
|
|
472
|
+
defaultValues: {
|
|
473
|
+
firstName: '',
|
|
474
|
+
lastName: '',
|
|
475
|
+
},
|
|
476
|
+
})
|
|
436
477
|
return (
|
|
437
478
|
<form.Provider>
|
|
438
479
|
<form.Field
|
|
@@ -461,4 +502,115 @@ describe('useField', () => {
|
|
|
461
502
|
const info = form!.fieldInfo
|
|
462
503
|
expect(Object.keys(info)).toHaveLength(0)
|
|
463
504
|
})
|
|
505
|
+
|
|
506
|
+
it('should handle strict mode properly with conditional fields', async () => {
|
|
507
|
+
function FieldInfo({ field }: { field: FieldApi<any, any> }) {
|
|
508
|
+
return (
|
|
509
|
+
<>
|
|
510
|
+
{field.state.meta.touchedErrors ? (
|
|
511
|
+
<em>{field.state.meta.touchedErrors}</em>
|
|
512
|
+
) : null}
|
|
513
|
+
{field.state.meta.isValidating ? 'Validating...' : null}
|
|
514
|
+
</>
|
|
515
|
+
)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function Comp() {
|
|
519
|
+
const [showField, setShowField] = React.useState(true)
|
|
520
|
+
|
|
521
|
+
const form = useForm({
|
|
522
|
+
defaultValues: {
|
|
523
|
+
firstName: '',
|
|
524
|
+
lastName: '',
|
|
525
|
+
},
|
|
526
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
527
|
+
onSubmit: async () => {},
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
return (
|
|
531
|
+
<div>
|
|
532
|
+
<form.Provider>
|
|
533
|
+
<form
|
|
534
|
+
onSubmit={(e) => {
|
|
535
|
+
e.preventDefault()
|
|
536
|
+
e.stopPropagation()
|
|
537
|
+
void form.handleSubmit()
|
|
538
|
+
}}
|
|
539
|
+
>
|
|
540
|
+
<div>
|
|
541
|
+
{/* A type-safe field component*/}
|
|
542
|
+
{showField ? (
|
|
543
|
+
<form.Field
|
|
544
|
+
name="firstName"
|
|
545
|
+
validators={{
|
|
546
|
+
onChange: ({ value }) =>
|
|
547
|
+
!value ? 'A first name is required' : undefined,
|
|
548
|
+
}}
|
|
549
|
+
children={(field) => {
|
|
550
|
+
// Avoid hasty abstractions. Render props are great!
|
|
551
|
+
return (
|
|
552
|
+
<>
|
|
553
|
+
<label htmlFor={field.name}>First Name:</label>
|
|
554
|
+
<input
|
|
555
|
+
name={field.name}
|
|
556
|
+
value={field.state.value}
|
|
557
|
+
onBlur={field.handleBlur}
|
|
558
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
559
|
+
/>
|
|
560
|
+
<FieldInfo field={field} />
|
|
561
|
+
</>
|
|
562
|
+
)
|
|
563
|
+
}}
|
|
564
|
+
/>
|
|
565
|
+
) : null}
|
|
566
|
+
</div>
|
|
567
|
+
<div>
|
|
568
|
+
<form.Field
|
|
569
|
+
name="lastName"
|
|
570
|
+
children={(field) => (
|
|
571
|
+
<>
|
|
572
|
+
<label htmlFor={field.name}>Last Name:</label>
|
|
573
|
+
<input
|
|
574
|
+
name={field.name}
|
|
575
|
+
value={field.state.value}
|
|
576
|
+
onBlur={field.handleBlur}
|
|
577
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
578
|
+
/>
|
|
579
|
+
<FieldInfo field={field} />
|
|
580
|
+
</>
|
|
581
|
+
)}
|
|
582
|
+
/>
|
|
583
|
+
</div>
|
|
584
|
+
<form.Subscribe
|
|
585
|
+
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
|
586
|
+
children={([canSubmit, isSubmitting]) => (
|
|
587
|
+
<button type="submit" disabled={!canSubmit}>
|
|
588
|
+
{isSubmitting ? '...' : 'Submit'}
|
|
589
|
+
</button>
|
|
590
|
+
)}
|
|
591
|
+
/>
|
|
592
|
+
<button
|
|
593
|
+
type="button"
|
|
594
|
+
onClick={() => setShowField((prev) => !prev)}
|
|
595
|
+
>
|
|
596
|
+
{showField ? 'Hide field' : 'Show field'}
|
|
597
|
+
</button>
|
|
598
|
+
</form>
|
|
599
|
+
</form.Provider>
|
|
600
|
+
</div>
|
|
601
|
+
)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const { getByText, findByText, queryByText } = render(
|
|
605
|
+
<React.StrictMode>
|
|
606
|
+
<Comp />
|
|
607
|
+
</React.StrictMode>,
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
await user.click(getByText('Submit'))
|
|
611
|
+
expect(await findByText('A first name is required')).toBeInTheDocument()
|
|
612
|
+
await user.click(getByText('Hide field'))
|
|
613
|
+
await user.click(getByText('Submit'))
|
|
614
|
+
expect(queryByText('A first name is required')).not.toBeInTheDocument()
|
|
615
|
+
})
|
|
464
616
|
})
|
|
@@ -3,7 +3,7 @@ import '@testing-library/jest-dom'
|
|
|
3
3
|
import { render, waitFor } from '@testing-library/react'
|
|
4
4
|
import userEvent from '@testing-library/user-event'
|
|
5
5
|
import * as React from 'react'
|
|
6
|
-
import { createFormFactory, useForm } from '
|
|
6
|
+
import { createFormFactory, useForm } from '../index'
|
|
7
7
|
import { sleep } from './utils'
|
|
8
8
|
|
|
9
9
|
const user = userEvent.setup()
|
|
@@ -172,6 +172,10 @@ describe('useForm', () => {
|
|
|
172
172
|
|
|
173
173
|
function Comp() {
|
|
174
174
|
const form = formFactory.useForm({
|
|
175
|
+
defaultValues: {
|
|
176
|
+
firstName: '',
|
|
177
|
+
lastName: '',
|
|
178
|
+
},
|
|
175
179
|
validators: {
|
|
176
180
|
onChange() {
|
|
177
181
|
return error
|
|
@@ -217,6 +221,10 @@ describe('useForm', () => {
|
|
|
217
221
|
|
|
218
222
|
function Comp() {
|
|
219
223
|
const form = formFactory.useForm({
|
|
224
|
+
defaultValues: {
|
|
225
|
+
firstName: '',
|
|
226
|
+
lastName: '',
|
|
227
|
+
},
|
|
220
228
|
validators: {
|
|
221
229
|
onChange: ({ value }) =>
|
|
222
230
|
value.firstName === 'other' ? error : undefined,
|
|
@@ -262,6 +270,10 @@ describe('useForm', () => {
|
|
|
262
270
|
|
|
263
271
|
function Comp() {
|
|
264
272
|
const form = formFactory.useForm({
|
|
273
|
+
defaultValues: {
|
|
274
|
+
firstName: '',
|
|
275
|
+
lastName: '',
|
|
276
|
+
},
|
|
265
277
|
validators: {
|
|
266
278
|
onChange: ({ value }) =>
|
|
267
279
|
value.firstName === 'other' ? error : undefined,
|
|
@@ -362,6 +374,10 @@ describe('useForm', () => {
|
|
|
362
374
|
|
|
363
375
|
function Comp() {
|
|
364
376
|
const form = formFactory.useForm({
|
|
377
|
+
defaultValues: {
|
|
378
|
+
firstName: '',
|
|
379
|
+
lastName: '',
|
|
380
|
+
},
|
|
365
381
|
validators: {
|
|
366
382
|
onChangeAsync: async () => {
|
|
367
383
|
await sleep(10)
|
|
@@ -412,6 +428,10 @@ describe('useForm', () => {
|
|
|
412
428
|
|
|
413
429
|
function Comp() {
|
|
414
430
|
const form = formFactory.useForm({
|
|
431
|
+
defaultValues: {
|
|
432
|
+
firstName: '',
|
|
433
|
+
lastName: '',
|
|
434
|
+
},
|
|
415
435
|
validators: {
|
|
416
436
|
onChangeAsync: async () => {
|
|
417
437
|
await sleep(10)
|
|
@@ -472,6 +492,10 @@ describe('useForm', () => {
|
|
|
472
492
|
|
|
473
493
|
function Comp() {
|
|
474
494
|
const form = formFactory.useForm({
|
|
495
|
+
defaultValues: {
|
|
496
|
+
firstName: '',
|
|
497
|
+
lastName: '',
|
|
498
|
+
},
|
|
475
499
|
validators: {
|
|
476
500
|
onChangeAsyncDebounceMs: 100,
|
|
477
501
|
onChangeAsync: async () => {
|
package/src/types.ts
CHANGED
package/src/useField.tsx
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState } from 'rehackt'
|
|
2
2
|
import { useStore } from '@tanstack/react-store'
|
|
3
|
+
import { FieldApi, functionalUpdate } from '@tanstack/form-core'
|
|
4
|
+
import { formContext, useFormContext } from './formContext'
|
|
5
|
+
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
|
|
6
|
+
import type { UseFieldOptions } from './types'
|
|
3
7
|
import type {
|
|
4
8
|
DeepKeys,
|
|
5
9
|
DeepValue,
|
|
6
10
|
Narrow,
|
|
7
11
|
Validator,
|
|
8
12
|
} from '@tanstack/form-core'
|
|
9
|
-
import { FieldApi, functionalUpdate } from '@tanstack/form-core'
|
|
10
|
-
import { useFormContext, formContext } from './formContext'
|
|
11
|
-
import type { UseFieldOptions } from './types'
|
|
12
|
-
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
|
|
13
|
-
import { useIsomorphicEffectOnce } from './useIsomorphicEffectOnce'
|
|
14
13
|
|
|
15
14
|
declare module '@tanstack/form-core' {
|
|
16
15
|
// eslint-disable-next-line no-shadow
|
|
@@ -88,6 +87,8 @@ export function useField<
|
|
|
88
87
|
return api
|
|
89
88
|
})
|
|
90
89
|
|
|
90
|
+
useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi])
|
|
91
|
+
|
|
91
92
|
/**
|
|
92
93
|
* fieldApi.update should not have any side effects. Think of it like a `useRef`
|
|
93
94
|
* that we need to keep updated every render with the most up-to-date information.
|
|
@@ -104,19 +105,6 @@ export function useField<
|
|
|
104
105
|
}
|
|
105
106
|
: undefined,
|
|
106
107
|
)
|
|
107
|
-
const unmountFn = useRef<(() => void) | null>(null)
|
|
108
|
-
|
|
109
|
-
useIsomorphicEffectOnce(() => {
|
|
110
|
-
return () => {
|
|
111
|
-
unmountFn.current?.()
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
// We have to mount it right as soon as it renders, otherwise we get:
|
|
116
|
-
// https://github.com/TanStack/form/issues/523
|
|
117
|
-
if (!unmountFn.current) {
|
|
118
|
-
unmountFn.current = fieldApi.mount()
|
|
119
|
-
}
|
|
120
108
|
|
|
121
109
|
return fieldApi as never
|
|
122
110
|
}
|
package/src/useForm.tsx
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import type { FormState, FormOptions, Validator } from '@tanstack/form-core'
|
|
2
1
|
import { FormApi, functionalUpdate } from '@tanstack/form-core'
|
|
3
|
-
import type { NoInfer } from '@tanstack/react-store'
|
|
4
2
|
import { useStore } from '@tanstack/react-store'
|
|
5
3
|
import React, {
|
|
6
4
|
type PropsWithChildren,
|
|
7
5
|
type ReactNode,
|
|
8
6
|
useState,
|
|
9
7
|
} from 'rehackt'
|
|
10
|
-
import {
|
|
8
|
+
import { Field, useField } from './useField'
|
|
11
9
|
import { formContext } from './formContext'
|
|
12
10
|
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
|
|
11
|
+
import type { NoInfer } from '@tanstack/react-store'
|
|
12
|
+
import type { FormOptions, FormState, Validator } from '@tanstack/form-core'
|
|
13
|
+
import type { FieldComponent, UseField } from './useField'
|
|
13
14
|
|
|
14
15
|
declare module '@tanstack/form-core' {
|
|
15
16
|
// eslint-disable-next-line no-shadow
|
|
@@ -21,6 +22,17 @@ declare module '@tanstack/form-core' {
|
|
|
21
22
|
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
|
|
22
23
|
) => TSelected
|
|
23
24
|
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
|
|
25
|
+
/**
|
|
26
|
+
TypeScript versions <=5.0.4 have a bug that prevents
|
|
27
|
+
the type of the `TSelected` generic from being inferred
|
|
28
|
+
from the return type of this method.
|
|
29
|
+
|
|
30
|
+
In these versions, `TSelected` will fall back to the default
|
|
31
|
+
type (or `unknown` if that's not defined).
|
|
32
|
+
|
|
33
|
+
@see {@link https://github.com/TanStack/form/pull/606/files#r1506715714 | This discussion on GitHub for the details}
|
|
34
|
+
@see {@link https://github.com/microsoft/TypeScript/issues/52786 | The bug report in `microsoft/TypeScript`}
|
|
35
|
+
*/
|
|
24
36
|
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
|
|
25
37
|
children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode
|
|
26
38
|
}) => JSX.Element
|
|
@@ -34,7 +46,6 @@ export function useForm<
|
|
|
34
46
|
opts?: FormOptions<TFormData, TFormValidator>,
|
|
35
47
|
): FormApi<TFormData, TFormValidator> {
|
|
36
48
|
const [formApi] = useState(() => {
|
|
37
|
-
// @ts-ignore
|
|
38
49
|
const api = new FormApi<TFormData, TFormValidator>(opts)
|
|
39
50
|
|
|
40
51
|
api.Provider = function Provider(props) {
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const rehackt = require("rehackt");
|
|
4
|
-
const useIsomorphicLayoutEffect = require("./useIsomorphicLayoutEffect.cjs");
|
|
5
|
-
const useIsomorphicEffectOnce = (effect) => {
|
|
6
|
-
const destroyFunc = rehackt.useRef();
|
|
7
|
-
const effectCalled = rehackt.useRef(false);
|
|
8
|
-
const renderAfterCalled = rehackt.useRef(false);
|
|
9
|
-
const [val, setVal] = rehackt.useState(0);
|
|
10
|
-
if (effectCalled.current) {
|
|
11
|
-
renderAfterCalled.current = true;
|
|
12
|
-
}
|
|
13
|
-
useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
|
|
14
|
-
if (!effectCalled.current) {
|
|
15
|
-
destroyFunc.current = effect();
|
|
16
|
-
effectCalled.current = true;
|
|
17
|
-
}
|
|
18
|
-
setVal((v) => v + 1);
|
|
19
|
-
return () => {
|
|
20
|
-
if (!renderAfterCalled.current) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
if (destroyFunc.current) {
|
|
24
|
-
destroyFunc.current();
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}, []);
|
|
28
|
-
};
|
|
29
|
-
exports.useIsomorphicEffectOnce = useIsomorphicEffectOnce;
|
|
30
|
-
//# sourceMappingURL=useIsomorphicEffectOnce.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useIsomorphicEffectOnce.cjs","sources":["../../src/useIsomorphicEffectOnce.ts"],"sourcesContent":["import { useRef, useState, type EffectCallback } from 'rehackt'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\n\n/**\n * This hook handles StrictMode and prod mode\n */\nexport const useIsomorphicEffectOnce = (effect: EffectCallback) => {\n const destroyFunc = useRef<void | (() => void)>()\n const effectCalled = useRef(false)\n const renderAfterCalled = useRef(false)\n const [val, setVal] = useState(0)\n\n if (effectCalled.current) {\n renderAfterCalled.current = true\n }\n\n useIsomorphicLayoutEffect(() => {\n // only execute the effect first time around\n if (!effectCalled.current) {\n destroyFunc.current = effect()\n effectCalled.current = true\n }\n\n // this forces one render after the effect is run\n setVal((v) => v + 1)\n\n return () => {\n // if the comp didn't render since the useEffect was called,\n // we know it's the dummy React cycle\n if (!renderAfterCalled.current) {\n return\n }\n if (destroyFunc.current) {\n destroyFunc.current()\n }\n }\n }, [])\n}\n"],"names":["useRef","useState","useIsomorphicLayoutEffect"],"mappings":";;;;AAMa,MAAA,0BAA0B,CAAC,WAA2B;AACjE,QAAM,cAAcA,QAAAA;AACd,QAAA,eAAeA,eAAO,KAAK;AAC3B,QAAA,oBAAoBA,eAAO,KAAK;AACtC,QAAM,CAAC,KAAK,MAAM,IAAIC,iBAAS,CAAC;AAEhC,MAAI,aAAa,SAAS;AACxB,sBAAkB,UAAU;AAAA,EAC9B;AAEAC,4BAAAA,0BAA0B,MAAM;AAE1B,QAAA,CAAC,aAAa,SAAS;AACzB,kBAAY,UAAU;AACtB,mBAAa,UAAU;AAAA,IACzB;AAGO,WAAA,CAAC,MAAM,IAAI,CAAC;AAEnB,WAAO,MAAM;AAGP,UAAA,CAAC,kBAAkB,SAAS;AAC9B;AAAA,MACF;AACA,UAAI,YAAY,SAAS;AACvB,oBAAY,QAAQ;AAAA,MACtB;AAAA,IAAA;AAAA,EAEJ,GAAG,CAAE,CAAA;AACP;;"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { useRef, useState } from "rehackt";
|
|
2
|
-
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
|
|
3
|
-
const useIsomorphicEffectOnce = (effect) => {
|
|
4
|
-
const destroyFunc = useRef();
|
|
5
|
-
const effectCalled = useRef(false);
|
|
6
|
-
const renderAfterCalled = useRef(false);
|
|
7
|
-
const [val, setVal] = useState(0);
|
|
8
|
-
if (effectCalled.current) {
|
|
9
|
-
renderAfterCalled.current = true;
|
|
10
|
-
}
|
|
11
|
-
useIsomorphicLayoutEffect(() => {
|
|
12
|
-
if (!effectCalled.current) {
|
|
13
|
-
destroyFunc.current = effect();
|
|
14
|
-
effectCalled.current = true;
|
|
15
|
-
}
|
|
16
|
-
setVal((v) => v + 1);
|
|
17
|
-
return () => {
|
|
18
|
-
if (!renderAfterCalled.current) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (destroyFunc.current) {
|
|
22
|
-
destroyFunc.current();
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}, []);
|
|
26
|
-
};
|
|
27
|
-
export {
|
|
28
|
-
useIsomorphicEffectOnce
|
|
29
|
-
};
|
|
30
|
-
//# sourceMappingURL=useIsomorphicEffectOnce.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useIsomorphicEffectOnce.js","sources":["../../src/useIsomorphicEffectOnce.ts"],"sourcesContent":["import { useRef, useState, type EffectCallback } from 'rehackt'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\n\n/**\n * This hook handles StrictMode and prod mode\n */\nexport const useIsomorphicEffectOnce = (effect: EffectCallback) => {\n const destroyFunc = useRef<void | (() => void)>()\n const effectCalled = useRef(false)\n const renderAfterCalled = useRef(false)\n const [val, setVal] = useState(0)\n\n if (effectCalled.current) {\n renderAfterCalled.current = true\n }\n\n useIsomorphicLayoutEffect(() => {\n // only execute the effect first time around\n if (!effectCalled.current) {\n destroyFunc.current = effect()\n effectCalled.current = true\n }\n\n // this forces one render after the effect is run\n setVal((v) => v + 1)\n\n return () => {\n // if the comp didn't render since the useEffect was called,\n // we know it's the dummy React cycle\n if (!renderAfterCalled.current) {\n return\n }\n if (destroyFunc.current) {\n destroyFunc.current()\n }\n }\n }, [])\n}\n"],"names":[],"mappings":";;AAMa,MAAA,0BAA0B,CAAC,WAA2B;AACjE,QAAM,cAAc;AACd,QAAA,eAAe,OAAO,KAAK;AAC3B,QAAA,oBAAoB,OAAO,KAAK;AACtC,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,CAAC;AAEhC,MAAI,aAAa,SAAS;AACxB,sBAAkB,UAAU;AAAA,EAC9B;AAEA,4BAA0B,MAAM;AAE1B,QAAA,CAAC,aAAa,SAAS;AACzB,kBAAY,UAAU;AACtB,mBAAa,UAAU;AAAA,IACzB;AAGO,WAAA,CAAC,MAAM,IAAI,CAAC;AAEnB,WAAO,MAAM;AAGP,UAAA,CAAC,kBAAkB,SAAS;AAC9B;AAAA,MACF;AACA,UAAI,YAAY,SAAS;AACvB,oBAAY,QAAQ;AAAA,MACtB;AAAA,IAAA;AAAA,EAEJ,GAAG,CAAE,CAAA;AACP;"}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { useRef, useState, type EffectCallback } from 'rehackt'
|
|
2
|
-
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* This hook handles StrictMode and prod mode
|
|
6
|
-
*/
|
|
7
|
-
export const useIsomorphicEffectOnce = (effect: EffectCallback) => {
|
|
8
|
-
const destroyFunc = useRef<void | (() => void)>()
|
|
9
|
-
const effectCalled = useRef(false)
|
|
10
|
-
const renderAfterCalled = useRef(false)
|
|
11
|
-
const [val, setVal] = useState(0)
|
|
12
|
-
|
|
13
|
-
if (effectCalled.current) {
|
|
14
|
-
renderAfterCalled.current = true
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
useIsomorphicLayoutEffect(() => {
|
|
18
|
-
// only execute the effect first time around
|
|
19
|
-
if (!effectCalled.current) {
|
|
20
|
-
destroyFunc.current = effect()
|
|
21
|
-
effectCalled.current = true
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// this forces one render after the effect is run
|
|
25
|
-
setVal((v) => v + 1)
|
|
26
|
-
|
|
27
|
-
return () => {
|
|
28
|
-
// if the comp didn't render since the useEffect was called,
|
|
29
|
-
// we know it's the dummy React cycle
|
|
30
|
-
if (!renderAfterCalled.current) {
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
if (destroyFunc.current) {
|
|
34
|
-
destroyFunc.current()
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}, [])
|
|
38
|
-
}
|