mirror of
https://github.com/LukeHagar/form.git
synced 2025-12-06 04:19:43 +00:00
fix: createFormFactory
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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> = {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
19
packages/react-form/src/createFormFactory.ts
Normal file
19
packages/react-form/src/createFormFactory.ts
Normal 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(),
|
||||
}
|
||||
}
|
||||
@@ -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!`)
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from '@tanstack/form-core'
|
||||
export * from './useForm'
|
||||
export * from './Field'
|
||||
export * from './useField'
|
||||
export * from './createFormFactory'
|
||||
|
||||
@@ -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 }),
|
||||
|
||||
@@ -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
3860
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user