mirror of
https://github.com/LukeHagar/form.git
synced 2025-12-09 12:27:44 +00:00
fix: complete form factory functionality, docs
This commit is contained in:
36
docs/framework/react/reference/createFormFactory.md
Normal file
36
docs/framework/react/reference/createFormFactory.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
id: createFormFactory
|
||||||
|
title: createFormFactory
|
||||||
|
---
|
||||||
|
|
||||||
|
### `createFormFactory`
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export function createFormFactory<TFormData>(
|
||||||
|
opts?: FormOptions<TFormData>,
|
||||||
|
): FormFactory<TFormData>
|
||||||
|
```
|
||||||
|
|
||||||
|
A function that creates a new `FormFactory<TFormData>` instance.
|
||||||
|
|
||||||
|
- `opts`
|
||||||
|
- Optional form options and a `listen` function to be called with the form state.
|
||||||
|
|
||||||
|
### `FormFactory<TFormData>`
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export type FormFactory<TFormData> = {
|
||||||
|
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
|
||||||
|
useField: UseField<TFormData>
|
||||||
|
Field: FieldComponent<TFormData>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `useForm`
|
||||||
|
- A custom hook that creates and returns a new instance of the `FormApi<TFormData>` class.
|
||||||
|
- `useField`
|
||||||
|
- A custom hook that returns an instance of the `FieldApi<TFormData>` class.
|
||||||
|
- `Field`
|
||||||
|
- A form field component.
|
||||||
@@ -35,10 +35,6 @@ An object representing the options for a form.
|
|||||||
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>
|
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>
|
||||||
```
|
```
|
||||||
- A function for custom validation logic for the form.
|
- A function for custom validation logic for the form.
|
||||||
- ```tsx
|
|
||||||
debugForm?: boolean
|
|
||||||
```
|
|
||||||
- A boolean flag to enable or disable form debugging.
|
|
||||||
- ```tsx
|
- ```tsx
|
||||||
defaultValidatePristine?: boolean
|
defaultValidatePristine?: boolean
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,10 +2,22 @@ import React from "react";
|
|||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { FieldApi, createFormFactory } from "@tanstack/react-form";
|
import { FieldApi, createFormFactory } from "@tanstack/react-form";
|
||||||
|
|
||||||
const formFactory = createFormFactory({
|
type Person = {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
hobbies: Hobby[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Hobby = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formFactory = createFormFactory<Person>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
firstName: "",
|
firstName: "",
|
||||||
lastName: "",
|
lastName: "",
|
||||||
|
hobbies: [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,6 +80,18 @@ export default function App() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<form.Field
|
||||||
|
name="hobbies"
|
||||||
|
children={(field) => (
|
||||||
|
<>
|
||||||
|
<label htmlFor={field.name}>Last Name:</label>
|
||||||
|
<input name={field.name} {...field.getInputProps()} />
|
||||||
|
<FieldInfo field={field} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<form.Subscribe
|
<form.Subscribe
|
||||||
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
||||||
children={([canSubmit, isSubmitting]) => (
|
children={([canSubmit, isSubmitting]) => (
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { FormEvent } from 'react'
|
|
||||||
import { Store } from '@tanstack/store'
|
import { Store } from '@tanstack/store'
|
||||||
//
|
//
|
||||||
import type { DeepKeys, DeepValue, Updater } from './utils'
|
import type { DeepKeys, DeepValue, Updater } from './utils'
|
||||||
@@ -11,7 +10,6 @@ export type FormOptions<TData> = {
|
|||||||
onSubmit?: (values: TData, formApi: FormApi<TData>) => void
|
onSubmit?: (values: TData, formApi: FormApi<TData>) => void
|
||||||
onInvalidSubmit?: (values: TData, formApi: FormApi<TData>) => void
|
onInvalidSubmit?: (values: TData, formApi: FormApi<TData>) => void
|
||||||
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>
|
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>
|
||||||
debugForm?: boolean
|
|
||||||
defaultValidatePristine?: boolean
|
defaultValidatePristine?: boolean
|
||||||
defaultValidateOn?: ValidationCause
|
defaultValidateOn?: ValidationCause
|
||||||
defaultValidateAsyncOn?: ValidationCause
|
defaultValidateAsyncOn?: ValidationCause
|
||||||
@@ -53,7 +51,7 @@ export type FormState<TData> = {
|
|||||||
submissionAttempts: number
|
submissionAttempts: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultFormState<TData>(
|
function getDefaultFormState<TData>(
|
||||||
defaultState: Partial<FormState<TData>>,
|
defaultState: Partial<FormState<TData>>,
|
||||||
): FormState<TData> {
|
): FormState<TData> {
|
||||||
return {
|
return {
|
||||||
@@ -247,7 +245,7 @@ export class FormApi<TFormData> {
|
|||||||
return this.validationMeta.validationPromise
|
return this.validationMeta.validationPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = async (e: FormEvent & { __handled?: boolean }) => {
|
handleSubmit = async (e: Event) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import type { FormApi, FormOptions } from '@tanstack/form-core'
|
import type { FormApi, FormOptions } from '@tanstack/form-core'
|
||||||
import { createUseField, type UseField } from './useField'
|
import { createUseField, type UseField } from './useField'
|
||||||
import { useForm } from './useForm'
|
import { useForm } from './useForm'
|
||||||
|
import { createFieldComponent, type FieldComponent } from './Field'
|
||||||
|
|
||||||
export type FormFactory<TFormData> = {
|
export type FormFactory<TFormData> = {
|
||||||
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
|
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
|
||||||
useField: UseField<TFormData>
|
useField: UseField<TFormData>
|
||||||
|
Field: FieldComponent<TFormData>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createFormFactory<TFormData>(
|
export function createFormFactory<TFormData>(
|
||||||
@@ -14,6 +16,7 @@ export function createFormFactory<TFormData>(
|
|||||||
useForm: (opts) => {
|
useForm: (opts) => {
|
||||||
return useForm<TFormData>({ ...defaultOpts, ...opts } as any) as any
|
return useForm<TFormData>({ ...defaultOpts, ...opts } as any) as any
|
||||||
},
|
},
|
||||||
useField: createUseField(),
|
useField: createUseField<TFormData>(),
|
||||||
|
Field: createFieldComponent<TFormData>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,35 @@
|
|||||||
export * from '@tanstack/form-core'
|
export type {
|
||||||
export * from './useForm'
|
ChangeProps,
|
||||||
export * from './Field'
|
DeepKeys,
|
||||||
export * from './useField'
|
DeepValue,
|
||||||
export * from './createFormFactory'
|
FieldApiOptions,
|
||||||
|
FieldInfo,
|
||||||
|
FieldMeta,
|
||||||
|
FieldOptions,
|
||||||
|
FieldState,
|
||||||
|
FormOptions,
|
||||||
|
FormState,
|
||||||
|
InputProps,
|
||||||
|
RequiredByKey,
|
||||||
|
Updater,
|
||||||
|
UpdaterFn,
|
||||||
|
UserChangeProps,
|
||||||
|
UserInputProps,
|
||||||
|
ValidationCause,
|
||||||
|
ValidationError,
|
||||||
|
ValidationMeta,
|
||||||
|
} from '@tanstack/form-core'
|
||||||
|
|
||||||
|
export { FormApi, FieldApi, functionalUpdate } from '@tanstack/form-core'
|
||||||
|
|
||||||
|
export type { FormComponent, FormProps } from './useForm'
|
||||||
|
export { useForm } from './useForm'
|
||||||
|
|
||||||
|
export type { FieldComponent } from './Field'
|
||||||
|
export { Field } from './Field'
|
||||||
|
|
||||||
|
export type { UseField } from './useField'
|
||||||
|
export { useField } from './useField'
|
||||||
|
|
||||||
|
export type { FormFactory } from './createFormFactory'
|
||||||
|
export { createFormFactory } from './createFormFactory'
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
|
|||||||
return api
|
return api
|
||||||
})
|
})
|
||||||
|
|
||||||
// React.useEffect(() => formApi.mount(), [])
|
|
||||||
|
|
||||||
return formApi as any
|
return formApi as any
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +64,7 @@ export type FormProps = React.HTMLProps<HTMLFormElement> & {
|
|||||||
|
|
||||||
export type FormComponent = (props: FormProps) => any
|
export type FormComponent = (props: FormProps) => any
|
||||||
|
|
||||||
export function createFormComponent(formApi: FormApi<any>) {
|
function createFormComponent(formApi: FormApi<any>) {
|
||||||
const Form: FormComponent = ({ children, noFormElement, ...rest }) => {
|
const Form: FormComponent = ({ children, noFormElement, ...rest }) => {
|
||||||
const isSubmitting = formApi.useStore((state) => state.isSubmitting)
|
const isSubmitting = formApi.useStore((state) => state.isSubmitting)
|
||||||
|
|
||||||
@@ -80,26 +78,6 @@ export function createFormComponent(formApi: FormApi<any>) {
|
|||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{formApi.options.debugForm ? (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
margin: '2rem 0',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontWeight: 'bolder',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Form State
|
|
||||||
</div>
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{JSON.stringify(formApi, safeStringifyReplace(), 2)}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{children}
|
{children}
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
@@ -109,16 +87,3 @@ export function createFormComponent(formApi: FormApi<any>) {
|
|||||||
|
|
||||||
return Form
|
return Form
|
||||||
}
|
}
|
||||||
|
|
||||||
function safeStringifyReplace() {
|
|
||||||
const set = new Set()
|
|
||||||
return (_key: string, value: any) => {
|
|
||||||
if (typeof value === 'object' || Array.isArray(value)) {
|
|
||||||
if (set.has(value)) {
|
|
||||||
return '(circular value)'
|
|
||||||
}
|
|
||||||
set.add(value)
|
|
||||||
}
|
|
||||||
return typeof value === 'function' ? undefined : value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user