import type { FormState, FormOptions } from '@tanstack/form-core' import { FormApi, functionalUpdate } from '@tanstack/form-core' import type { NoInfer } from '@tanstack/react-store' import { useStore } from '@tanstack/react-store' import React, { type ReactNode, useState } from 'react' import { type UseField, type FieldComponent, Field, useField } from './useField' import { formContext } from './formContext' import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect' declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow interface FormApi { Provider: (props: { children: any }) => any Field: FieldComponent useField: UseField useStore: >>( selector?: (state: NoInfer>) => TSelected, ) => TSelected Subscribe: >>(props: { selector?: (state: NoInfer>) => TSelected children: ((state: NoInfer) => ReactNode) | ReactNode }) => any } } export function useForm(opts?: FormOptions): FormApi { const [formApi] = useState(() => { // @ts-ignore const api = new FormApi(opts) api.Provider = function Provider(props) { return } api.Field = Field as any api.useField = useField as any api.useStore = ( // @ts-ignore selector, ) => { // eslint-disable-next-line react-hooks/rules-of-hooks return useStore(api.store as any, selector as any) as any } api.Subscribe = ( // @ts-ignore props, ) => { return functionalUpdate( props.children, // eslint-disable-next-line react-hooks/rules-of-hooks useStore(api.store as any, props.selector as any), ) as any } return api }) formApi.useStore((state) => state.isSubmitting) /** * formApi.update should not have any side effects. Think of it like a `useRef` * that we need to keep updated every render with the most up-to-date information. */ useIsomorphicLayoutEffect(() => { formApi.update(opts) }) return formApi as any }