Skip to content

Commit b274bcc

Browse files
committed
fix: createFormFactory
1 parent 0947516 commit b274bcc

File tree

10 files changed

+3673
-343
lines changed

10 files changed

+3673
-343
lines changed

examples/react/simple/src/index.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import React from "react";
22
import ReactDOM from "react-dom/client";
3-
import { useForm, FieldApi } from "@tanstack/react-form";
3+
import { FieldApi, createFormFactory } from "@tanstack/react-form";
4+
5+
const formFactory = createFormFactory({
6+
defaultValues: {
7+
firstName: "",
8+
lastName: "",
9+
},
10+
});
411

512
function FieldInfo({ field }: { field: FieldApi<any, any> }) {
613
return (
@@ -14,15 +21,7 @@ function FieldInfo({ field }: { field: FieldApi<any, any> }) {
1421
}
1522

1623
export default function App() {
17-
const form = useForm({
18-
// Memoize your default values to prevent re-renders
19-
defaultValues: React.useMemo(
20-
() => ({
21-
firstName: "",
22-
lastName: "",
23-
}),
24-
[]
25-
),
24+
const form = formFactory.useForm({
2625
onSubmit: async (values) => {
2726
// Do something with form data
2827
console.log(values);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
"stream-to-array": "^2.3.0",
106106
"ts-jest": "^27.1.1",
107107
"ts-node": "^10.7.0",
108-
"typescript": "^4.7.4",
108+
"typescript": "^5.0.4",
109109
"vue": "^3.2.33"
110110
},
111111
"bundlewatch": {

packages/form-core/src/FieldApi.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ import { Store } from '@tanstack/store'
55

66
export type ValidationCause = 'change' | 'blur' | 'submit'
77

8-
export type FieldOptions<TData, TFormData> = {
8+
export interface FieldOptions<TData, TFormData> {
99
name: unknown extends TFormData ? string : DeepKeys<TFormData>
1010
defaultValue?: TData
11-
form?: FormApi<TFormData>
1211
validate?: (
1312
value: TData,
1413
fieldApi: FieldApi<TData, TFormData>,
@@ -24,6 +23,13 @@ export type FieldOptions<TData, TFormData> = {
2423
defaultMeta?: Partial<FieldMeta>
2524
}
2625

26+
export type FieldApiOptions<TData, TFormData> = FieldOptions<
27+
TData,
28+
TFormData
29+
> & {
30+
form: FormApi<TFormData>
31+
}
32+
2733
export type FieldMeta = {
2834
isTouched: boolean
2935
touchedError?: ValidationError
@@ -53,11 +59,6 @@ export type InputProps = {
5359
onBlur: (event: any) => void
5460
}
5561

56-
export type FieldApiOptions<TData, TFormData> = RequiredByKey<
57-
FieldOptions<TData, TFormData>,
58-
'form'
59-
>
60-
6162
let uid = 0
6263

6364
export type FieldState<TData> = {

packages/react-form/src/Field.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
type DeepValue,
66
type FieldApi,
77
type FieldOptions,
8-
type FormApi,
98
} from '@tanstack/form-core'
109
import { useField } from './useField'
1110

@@ -19,9 +18,9 @@ export type FieldComponent<TFormData> = <TField extends DeepKeys<TFormData>>({
1918
name: TField
2019
} & Omit<FieldOptions<DeepValue<TFormData, TField>, TFormData>, 'name'>) => any
2120

22-
export function createFieldComponent<TFormData>(formApi: FormApi<TFormData>) {
21+
export function createFieldComponent<TFormData>() {
2322
const ConnectedField: FieldComponent<TFormData> = (props) => (
24-
<Field {...(props as any)} form={formApi} />
23+
<Field {...(props as any)} />
2524
)
2625
return ConnectedField
2726
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { FormApi, FormOptions } from '@tanstack/form-core'
2+
import { createUseField, type UseField } from './useField'
3+
import { useForm } from './useForm'
4+
5+
export type FormFactory<TFormData> = {
6+
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
7+
useField: UseField<TFormData>
8+
}
9+
10+
export function createFormFactory<TFormData>(
11+
defaultOpts?: FormOptions<TFormData>,
12+
): FormFactory<TFormData> {
13+
return {
14+
useForm: (opts) => {
15+
return useForm<TFormData>({ ...defaultOpts, ...opts } as any) as any
16+
},
17+
useField: createUseField(),
18+
}
19+
}

packages/react-form/src/formContext.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@ import * as React from 'react'
33

44
export const formContext = React.createContext<FormApi<any> | null>(null)
55

6-
export function useFormContext(customFormApi?: FormApi<any>) {
6+
export function useFormContext() {
77
const formApi = React.useContext(formContext)
88

9-
if (customFormApi) {
10-
return customFormApi
11-
}
12-
139
if (!formApi) {
1410
throw new Error(`You are trying to use the form API outside of a form!`)
1511
}

packages/react-form/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from '@tanstack/form-core'
22
export * from './useForm'
33
export * from './Field'
44
export * from './useField'
5+
export * from './createFormFactory'

packages/react-form/src/useField.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import * as React from 'react'
22
//
33
import { useStore } from '@tanstack/react-store'
4-
import type {
5-
DeepKeys,
6-
DeepValue,
7-
FieldOptions,
8-
FormApi,
9-
} from '@tanstack/form-core'
4+
import type { DeepKeys, DeepValue, FieldOptions } from '@tanstack/form-core'
105
import { FieldApi } from '@tanstack/form-core'
116
import { useFormContext } from './formContext'
7+
import type { FormFactory } from './createFormFactory'
8+
9+
declare module '@tanstack/form-core' {
10+
// eslint-disable-next-line no-shadow
11+
interface FieldOptions<TData, TFormData> {
12+
formFactory?: FormFactory<TFormData>
13+
}
14+
}
1215

1316
export type UseField<TFormData> = <TField extends DeepKeys<TFormData>>(
1417
opts?: { name: TField } & FieldOptions<
@@ -17,26 +20,19 @@ export type UseField<TFormData> = <TField extends DeepKeys<TFormData>>(
1720
>,
1821
) => FieldApi<DeepValue<TFormData, TField>, TFormData>
1922

20-
export function createUseField<TFormData>(formApi: FormApi<TFormData>) {
21-
const useFormField: UseField<TFormData> = (opts) => {
22-
return useField({ ...opts, form: formApi } as any)
23+
export function createUseField<TFormData>(): UseField<TFormData> {
24+
return (opts) => {
25+
return useField(opts as any)
2326
}
24-
25-
return useFormField
2627
}
2728

2829
export function useField<TData, TFormData>(
2930
opts: FieldOptions<TData, TFormData> & {
3031
// selector: (state: FieldApi<TData, TFormData>) => TSelected
3132
},
3233
): FieldApi<TData, TFormData> {
33-
// invariant( // TODO:
34-
// opts.name,
35-
// `useField: A field is required to use this hook. eg, useField('myField', options)`
36-
// )
37-
3834
// Get the form API either manually or from context
39-
const formApi = useFormContext(opts.form)
35+
const formApi = useFormContext()
4036

4137
const [fieldApi] = React.useState<FieldApi<TData, TFormData>>(
4238
() => new FieldApi({ ...opts, form: formApi }),

packages/react-form/src/useForm.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,21 @@ declare module '@tanstack/form-core' {
2424
}) => any
2525
}
2626
}
27-
//
2827

2928
export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
3029
const [formApi] = React.useState(() => {
3130
// @ts-ignore
32-
const api = new FormApi<TData>(opts || {})
31+
const api = new FormApi<TData>(opts)
3332

3433
api.Form = createFormComponent(api)
35-
api.Field = createFieldComponent(api)
36-
api.useField = createUseField(api)
34+
api.Field = createFieldComponent<TData>()
35+
api.useField = createUseField<TData>()
3736
api.useStore = (
3837
// @ts-ignore
3938
selector,
4039
) => {
4140
// eslint-disable-next-line react-hooks/rules-of-hooks
42-
return useStore(api.store, selector) as any
41+
return useStore(api.store, selector as any) as any
4342
}
4443
api.Subscribe = (
4544
// @ts-ignore
@@ -48,7 +47,7 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
4847
return functionalUpdate(
4948
props.children,
5049
// eslint-disable-next-line react-hooks/rules-of-hooks
51-
useStore(api.store, props.selector),
50+
useStore(api.store, props.selector as any),
5251
) as any
5352
}
5453

@@ -57,7 +56,7 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
5756

5857
// React.useEffect(() => formApi.mount(), [])
5958

60-
return formApi
59+
return formApi as any
6160
}
6261

6362
export type FormProps = React.HTMLProps<HTMLFormElement> & {

0 commit comments

Comments
 (0)