Skip to content

Commit c444704

Browse files
committed
fix: complete form factory functionality, docs
1 parent b274bcc commit c444704

File tree

7 files changed

+103
-51
lines changed

7 files changed

+103
-51
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
id: createFormFactory
3+
title: createFormFactory
4+
---
5+
6+
### `createFormFactory`
7+
8+
```tsx
9+
export function createFormFactory<TFormData>(
10+
opts?: FormOptions<TFormData>,
11+
): FormFactory<TFormData>
12+
```
13+
14+
A function that creates a new `FormFactory<TFormData>` instance.
15+
16+
- `opts`
17+
- Optional form options and a `listen` function to be called with the form state.
18+
19+
### `FormFactory<TFormData>`
20+
21+
A type representing a form factory. Form factories provide a type-safe way to interact with the form API as opposed to using the globally exported form utilities.
22+
23+
```tsx
24+
export type FormFactory<TFormData> = {
25+
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
26+
useField: UseField<TFormData>
27+
Field: FieldComponent<TFormData>
28+
}
29+
```
30+
31+
- `useForm`
32+
- A custom hook that creates and returns a new instance of the `FormApi<TFormData>` class.
33+
- `useField`
34+
- A custom hook that returns an instance of the `FieldApi<TFormData>` class.
35+
- `Field`
36+
- A form field component.

docs/reference/formApi.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ An object representing the options for a form.
3535
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>
3636
```
3737
- A function for custom validation logic for the form.
38-
- ```tsx
39-
debugForm?: boolean
40-
```
41-
- A boolean flag to enable or disable form debugging.
4238
- ```tsx
4339
defaultValidatePristine?: boolean
4440
```

examples/react/simple/src/index.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,22 @@ import React from "react";
22
import ReactDOM from "react-dom/client";
33
import { FieldApi, createFormFactory } from "@tanstack/react-form";
44

5-
const formFactory = createFormFactory({
5+
type Person = {
6+
firstName: string;
7+
lastName: string;
8+
hobbies: Hobby[];
9+
};
10+
11+
type Hobby = {
12+
name: string;
13+
description: string;
14+
};
15+
16+
const formFactory = createFormFactory<Person>({
617
defaultValues: {
718
firstName: "",
819
lastName: "",
20+
hobbies: [],
921
},
1022
});
1123

@@ -68,6 +80,18 @@ export default function App() {
6880
)}
6981
/>
7082
</div>
83+
<div>
84+
<form.Field
85+
name="hobbies"
86+
children={(field) => (
87+
<>
88+
<label htmlFor={field.name}>Last Name:</label>
89+
<input name={field.name} {...field.getInputProps()} />
90+
<FieldInfo field={field} />
91+
</>
92+
)}
93+
/>
94+
</div>
7195
<form.Subscribe
7296
selector={(state) => [state.canSubmit, state.isSubmitting]}
7397
children={([canSubmit, isSubmitting]) => (

packages/form-core/src/FormApi.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { FormEvent } from 'react'
21
import { Store } from '@tanstack/store'
32
//
43
import type { DeepKeys, DeepValue, Updater } from './utils'
@@ -11,7 +10,6 @@ export type FormOptions<TData> = {
1110
onSubmit?: (values: TData, formApi: FormApi<TData>) => void
1211
onInvalidSubmit?: (values: TData, formApi: FormApi<TData>) => void
1312
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>
14-
debugForm?: boolean
1513
defaultValidatePristine?: boolean
1614
defaultValidateOn?: ValidationCause
1715
defaultValidateAsyncOn?: ValidationCause
@@ -53,7 +51,7 @@ export type FormState<TData> = {
5351
submissionAttempts: number
5452
}
5553

56-
export function getDefaultFormState<TData>(
54+
function getDefaultFormState<TData>(
5755
defaultState: Partial<FormState<TData>>,
5856
): FormState<TData> {
5957
return {
@@ -247,7 +245,7 @@ export class FormApi<TFormData> {
247245
return this.validationMeta.validationPromise
248246
}
249247

250-
handleSubmit = async (e: FormEvent & { __handled?: boolean }) => {
248+
handleSubmit = async (e: Event) => {
251249
e.preventDefault()
252250
e.stopPropagation()
253251

packages/react-form/src/createFormFactory.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import type { FormApi, FormOptions } from '@tanstack/form-core'
22
import { createUseField, type UseField } from './useField'
33
import { useForm } from './useForm'
4+
import { createFieldComponent, type FieldComponent } from './Field'
45

56
export type FormFactory<TFormData> = {
67
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
78
useField: UseField<TFormData>
9+
Field: FieldComponent<TFormData>
810
}
911

1012
export function createFormFactory<TFormData>(
@@ -14,6 +16,7 @@ export function createFormFactory<TFormData>(
1416
useForm: (opts) => {
1517
return useForm<TFormData>({ ...defaultOpts, ...opts } as any) as any
1618
},
17-
useField: createUseField(),
19+
useField: createUseField<TFormData>(),
20+
Field: createFieldComponent<TFormData>(),
1821
}
1922
}

packages/react-form/src/index.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
1-
export * from '@tanstack/form-core'
2-
export * from './useForm'
3-
export * from './Field'
4-
export * from './useField'
5-
export * from './createFormFactory'
1+
export type {
2+
ChangeProps,
3+
DeepKeys,
4+
DeepValue,
5+
FieldApiOptions,
6+
FieldInfo,
7+
FieldMeta,
8+
FieldOptions,
9+
FieldState,
10+
FormOptions,
11+
FormState,
12+
InputProps,
13+
RequiredByKey,
14+
Updater,
15+
UpdaterFn,
16+
UserChangeProps,
17+
UserInputProps,
18+
ValidationCause,
19+
ValidationError,
20+
ValidationMeta,
21+
} from '@tanstack/form-core'
22+
23+
export { FormApi, FieldApi, functionalUpdate } from '@tanstack/form-core'
24+
25+
export type { FormComponent, FormProps } from './useForm'
26+
export { useForm } from './useForm'
27+
28+
export type { FieldComponent } from './Field'
29+
export { Field } from './Field'
30+
31+
export type { UseField } from './useField'
32+
export { useField } from './useField'
33+
34+
export type { FormFactory } from './createFormFactory'
35+
export { createFormFactory } from './createFormFactory'

packages/react-form/src/useForm.tsx

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
5454
return api
5555
})
5656

57-
// React.useEffect(() => formApi.mount(), [])
58-
5957
return formApi as any
6058
}
6159

@@ -66,7 +64,7 @@ export type FormProps = React.HTMLProps<HTMLFormElement> & {
6664

6765
export type FormComponent = (props: FormProps) => any
6866

69-
export function createFormComponent(formApi: FormApi<any>) {
67+
function createFormComponent(formApi: FormApi<any>) {
7068
const Form: FormComponent = ({ children, noFormElement, ...rest }) => {
7169
const isSubmitting = formApi.useStore((state) => state.isSubmitting)
7270

@@ -80,26 +78,6 @@ export function createFormComponent(formApi: FormApi<any>) {
8078
disabled={isSubmitting}
8179
{...rest}
8280
>
83-
{formApi.options.debugForm ? (
84-
<div
85-
style={{
86-
margin: '2rem 0',
87-
}}
88-
>
89-
<div
90-
style={{
91-
fontWeight: 'bolder',
92-
}}
93-
>
94-
Form State
95-
</div>
96-
<pre>
97-
<code>
98-
{JSON.stringify(formApi, safeStringifyReplace(), 2)}
99-
</code>
100-
</pre>
101-
</div>
102-
) : null}
10381
{children}
10482
</form>
10583
)}
@@ -109,16 +87,3 @@ export function createFormComponent(formApi: FormApi<any>) {
10987

11088
return Form
11189
}
112-
113-
function safeStringifyReplace() {
114-
const set = new Set()
115-
return (_key: string, value: any) => {
116-
if (typeof value === 'object' || Array.isArray(value)) {
117-
if (set.has(value)) {
118-
return '(circular value)'
119-
}
120-
set.add(value)
121-
}
122-
return typeof value === 'function' ? undefined : value
123-
}
124-
}

0 commit comments

Comments
 (0)