mirror of
https://github.com/LukeHagar/form.git
synced 2025-12-06 04:19:43 +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>
|
||||
```
|
||||
- A function for custom validation logic for the form.
|
||||
- ```tsx
|
||||
debugForm?: boolean
|
||||
```
|
||||
- A boolean flag to enable or disable form debugging.
|
||||
- ```tsx
|
||||
defaultValidatePristine?: boolean
|
||||
```
|
||||
|
||||
@@ -2,10 +2,22 @@ import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
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: {
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
hobbies: [],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -68,6 +80,18 @@ export default function App() {
|
||||
)}
|
||||
/>
|
||||
</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
|
||||
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
||||
children={([canSubmit, isSubmitting]) => (
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { FormEvent } from 'react'
|
||||
import { Store } from '@tanstack/store'
|
||||
//
|
||||
import type { DeepKeys, DeepValue, Updater } from './utils'
|
||||
@@ -11,7 +10,6 @@ export type FormOptions<TData> = {
|
||||
onSubmit?: (values: TData, formApi: FormApi<TData>) => void
|
||||
onInvalidSubmit?: (values: TData, formApi: FormApi<TData>) => void
|
||||
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>
|
||||
debugForm?: boolean
|
||||
defaultValidatePristine?: boolean
|
||||
defaultValidateOn?: ValidationCause
|
||||
defaultValidateAsyncOn?: ValidationCause
|
||||
@@ -53,7 +51,7 @@ export type FormState<TData> = {
|
||||
submissionAttempts: number
|
||||
}
|
||||
|
||||
export function getDefaultFormState<TData>(
|
||||
function getDefaultFormState<TData>(
|
||||
defaultState: Partial<FormState<TData>>,
|
||||
): FormState<TData> {
|
||||
return {
|
||||
@@ -247,7 +245,7 @@ export class FormApi<TFormData> {
|
||||
return this.validationMeta.validationPromise
|
||||
}
|
||||
|
||||
handleSubmit = async (e: FormEvent & { __handled?: boolean }) => {
|
||||
handleSubmit = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { FormApi, FormOptions } from '@tanstack/form-core'
|
||||
import { createUseField, type UseField } from './useField'
|
||||
import { useForm } from './useForm'
|
||||
import { createFieldComponent, type FieldComponent } from './Field'
|
||||
|
||||
export type FormFactory<TFormData> = {
|
||||
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
|
||||
useField: UseField<TFormData>
|
||||
Field: FieldComponent<TFormData>
|
||||
}
|
||||
|
||||
export function createFormFactory<TFormData>(
|
||||
@@ -14,6 +16,7 @@ export function createFormFactory<TFormData>(
|
||||
useForm: (opts) => {
|
||||
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 * from './useForm'
|
||||
export * from './Field'
|
||||
export * from './useField'
|
||||
export * from './createFormFactory'
|
||||
export type {
|
||||
ChangeProps,
|
||||
DeepKeys,
|
||||
DeepValue,
|
||||
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
|
||||
})
|
||||
|
||||
// React.useEffect(() => formApi.mount(), [])
|
||||
|
||||
return formApi as any
|
||||
}
|
||||
|
||||
@@ -66,7 +64,7 @@ export type FormProps = React.HTMLProps<HTMLFormElement> & {
|
||||
|
||||
export type FormComponent = (props: FormProps) => any
|
||||
|
||||
export function createFormComponent(formApi: FormApi<any>) {
|
||||
function createFormComponent(formApi: FormApi<any>) {
|
||||
const Form: FormComponent = ({ children, noFormElement, ...rest }) => {
|
||||
const isSubmitting = formApi.useStore((state) => state.isSubmitting)
|
||||
|
||||
@@ -80,26 +78,6 @@ export function createFormComponent(formApi: FormApi<any>) {
|
||||
disabled={isSubmitting}
|
||||
{...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}
|
||||
</form>
|
||||
)}
|
||||
@@ -109,16 +87,3 @@ export function createFormComponent(formApi: FormApi<any>) {
|
||||
|
||||
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