fix: createFormFactory

This commit is contained in:
Tanner Linsley
2023-05-01 10:27:51 -06:00
parent 09475161af
commit b274bccf3d
10 changed files with 3650 additions and 320 deletions

View File

@@ -1,6 +1,13 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { useForm, FieldApi } from "@tanstack/react-form";
import { FieldApi, createFormFactory } from "@tanstack/react-form";
const formFactory = createFormFactory({
defaultValues: {
firstName: "",
lastName: "",
},
});
function FieldInfo({ field }: { field: FieldApi<any, any> }) {
return (
@@ -14,15 +21,7 @@ function FieldInfo({ field }: { field: FieldApi<any, any> }) {
}
export default function App() {
const form = useForm({
// Memoize your default values to prevent re-renders
defaultValues: React.useMemo(
() => ({
firstName: "",
lastName: "",
}),
[]
),
const form = formFactory.useForm({
onSubmit: async (values) => {
// Do something with form data
console.log(values);

View File

@@ -105,7 +105,7 @@
"stream-to-array": "^2.3.0",
"ts-jest": "^27.1.1",
"ts-node": "^10.7.0",
"typescript": "^4.7.4",
"typescript": "^5.0.4",
"vue": "^3.2.33"
},
"bundlewatch": {

View File

@@ -5,10 +5,9 @@ import { Store } from '@tanstack/store'
export type ValidationCause = 'change' | 'blur' | 'submit'
export type FieldOptions<TData, TFormData> = {
export interface FieldOptions<TData, TFormData> {
name: unknown extends TFormData ? string : DeepKeys<TFormData>
defaultValue?: TData
form?: FormApi<TFormData>
validate?: (
value: TData,
fieldApi: FieldApi<TData, TFormData>,
@@ -24,6 +23,13 @@ export type FieldOptions<TData, TFormData> = {
defaultMeta?: Partial<FieldMeta>
}
export type FieldApiOptions<TData, TFormData> = FieldOptions<
TData,
TFormData
> & {
form: FormApi<TFormData>
}
export type FieldMeta = {
isTouched: boolean
touchedError?: ValidationError
@@ -53,11 +59,6 @@ export type InputProps = {
onBlur: (event: any) => void
}
export type FieldApiOptions<TData, TFormData> = RequiredByKey<
FieldOptions<TData, TFormData>,
'form'
>
let uid = 0
export type FieldState<TData> = {

View File

@@ -5,7 +5,6 @@ import {
type DeepValue,
type FieldApi,
type FieldOptions,
type FormApi,
} from '@tanstack/form-core'
import { useField } from './useField'
@@ -19,9 +18,9 @@ export type FieldComponent<TFormData> = <TField extends DeepKeys<TFormData>>({
name: TField
} & Omit<FieldOptions<DeepValue<TFormData, TField>, TFormData>, 'name'>) => any
export function createFieldComponent<TFormData>(formApi: FormApi<TFormData>) {
export function createFieldComponent<TFormData>() {
const ConnectedField: FieldComponent<TFormData> = (props) => (
<Field {...(props as any)} form={formApi} />
<Field {...(props as any)} />
)
return ConnectedField
}

View File

@@ -0,0 +1,19 @@
import type { FormApi, FormOptions } from '@tanstack/form-core'
import { createUseField, type UseField } from './useField'
import { useForm } from './useForm'
export type FormFactory<TFormData> = {
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
useField: UseField<TFormData>
}
export function createFormFactory<TFormData>(
defaultOpts?: FormOptions<TFormData>,
): FormFactory<TFormData> {
return {
useForm: (opts) => {
return useForm<TFormData>({ ...defaultOpts, ...opts } as any) as any
},
useField: createUseField(),
}
}

View File

@@ -3,13 +3,9 @@ import * as React from 'react'
export const formContext = React.createContext<FormApi<any> | null>(null)
export function useFormContext(customFormApi?: FormApi<any>) {
export function useFormContext() {
const formApi = React.useContext(formContext)
if (customFormApi) {
return customFormApi
}
if (!formApi) {
throw new Error(`You are trying to use the form API outside of a form!`)
}

View File

@@ -2,3 +2,4 @@ export * from '@tanstack/form-core'
export * from './useForm'
export * from './Field'
export * from './useField'
export * from './createFormFactory'

View File

@@ -1,14 +1,17 @@
import * as React from 'react'
//
import { useStore } from '@tanstack/react-store'
import type {
DeepKeys,
DeepValue,
FieldOptions,
FormApi,
} from '@tanstack/form-core'
import type { DeepKeys, DeepValue, FieldOptions } from '@tanstack/form-core'
import { FieldApi } from '@tanstack/form-core'
import { useFormContext } from './formContext'
import type { FormFactory } from './createFormFactory'
declare module '@tanstack/form-core' {
// eslint-disable-next-line no-shadow
interface FieldOptions<TData, TFormData> {
formFactory?: FormFactory<TFormData>
}
}
export type UseField<TFormData> = <TField extends DeepKeys<TFormData>>(
opts?: { name: TField } & FieldOptions<
@@ -17,12 +20,10 @@ export type UseField<TFormData> = <TField extends DeepKeys<TFormData>>(
>,
) => FieldApi<DeepValue<TFormData, TField>, TFormData>
export function createUseField<TFormData>(formApi: FormApi<TFormData>) {
const useFormField: UseField<TFormData> = (opts) => {
return useField({ ...opts, form: formApi } as any)
export function createUseField<TFormData>(): UseField<TFormData> {
return (opts) => {
return useField(opts as any)
}
return useFormField
}
export function useField<TData, TFormData>(
@@ -30,13 +31,8 @@ export function useField<TData, TFormData>(
// selector: (state: FieldApi<TData, TFormData>) => TSelected
},
): FieldApi<TData, TFormData> {
// invariant( // TODO:
// opts.name,
// `useField: A field is required to use this hook. eg, useField('myField', options)`
// )
// Get the form API either manually or from context
const formApi = useFormContext(opts.form)
const formApi = useFormContext()
const [fieldApi] = React.useState<FieldApi<TData, TFormData>>(
() => new FieldApi({ ...opts, form: formApi }),

View File

@@ -24,22 +24,21 @@ declare module '@tanstack/form-core' {
}) => any
}
}
//
export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
const [formApi] = React.useState(() => {
// @ts-ignore
const api = new FormApi<TData>(opts || {})
const api = new FormApi<TData>(opts)
api.Form = createFormComponent(api)
api.Field = createFieldComponent(api)
api.useField = createUseField(api)
api.Field = createFieldComponent<TData>()
api.useField = createUseField<TData>()
api.useStore = (
// @ts-ignore
selector,
) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
return useStore(api.store, selector) as any
return useStore(api.store, selector as any) as any
}
api.Subscribe = (
// @ts-ignore
@@ -48,7 +47,7 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
return functionalUpdate(
props.children,
// eslint-disable-next-line react-hooks/rules-of-hooks
useStore(api.store, props.selector),
useStore(api.store, props.selector as any),
) as any
}
@@ -57,7 +56,7 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
// React.useEffect(() => formApi.mount(), [])
return formApi
return formApi as any
}
export type FormProps = React.HTMLProps<HTMLFormElement> & {

3860
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff