import React , { Reducer , useReducer , ReactNode , useMemo , useRef , useEffect , createContext , } from 'react' ; import invariant from 'invariant' ; import { ValidateRule , Wrapped , FormErrors , FormTouched , FormValidators , FormActions , ActionEnums , FormContext , FormValidating , FormChangeEvent , FormChildrenProps , FormStoreProps , } from '../types' ; import useValidator from '../hooks/useValidator' ; import { FormContextProvider } from '../internals/context' ; type FormValues = { [ key in string ] : any ; } ; /**
 * Props provided to <Form />
 */ type FormProps < V > = { initialValues : V ; initialTouched ? : FormTouched < V > ; initialErrors ? : FormErrors < V > ; validationSchema ? : { [ K in keyof V ] ? : Wrapped < ValidateRule < V [ K ] , FormContext < V > > > ; } ; } ; function Form < Values extends FormValues > ( { initialValues , initialErrors , initialTouched , children , validationSchema , } ) { const initialValidating = Object . keys ( initialValues ) . reduce ( ( prev , fieldName ) => { prev [ fieldName ] = false ; return prev ; } , { } ) as FormValidating < Values > ; const [ state , dispatch ] = useReducer < Reducer < FormStoreProps < Values > , FormActions < Values > > > ( ( state , { type , payload } ) => { switch ( type ) { case ActionEnums . SET_ERRORS : return { ... state , errors : { ... state . errors , ... payload } } ; case ActionEnums . SET_FIELD : return { ... state , values : { ... state . values , ... payload } } ; case ActionEnums . SET_VALIDATING_FIELD : { return { ... state , validating : { ... state . validating , ... payload } } ; } default : invariant ( false , 'you provided an unkown action!' ) ; } } , { values : initialValues , touched : { } , errors : { ... initialErrors } , validating : initialValidating , isValid : true , } ) ; const validationContext = useRef < FormContext < Values > > ( { state , dispatch , } ) ; useEffect ( ( ) => { validationContext . current = { state , dispatch } ; } ) ; const validators = useMemo ( ( ) => { const vals = { } ; Object . keys ( initialValues ) . map ( ( fieldName ) => { const validator = useValidator ( fieldName , validationSchema , validationContext ) ; vals [ fieldName ] = validator ; } ) ; return vals ; } , [ validationSchema ] ) as FormValidators < Values > ; const handleChange = useMemo ( ( ) => { const handlers = { } ; Object . keys ( initialValues ) . map ( ( fieldName ) => { handlers [ fieldName ] = ( val ) => dispatch ( { type : ActionEnums . SET_FIELD , payload : { [ fieldName ] : val , } , } as FormActions < Values > ) ; } ) ; return handlers ; } , [ initialValues ] ) as FormChangeEvent < Values > ; return ( < FormContextProvider value = { { state , dispatch } } > { children ( { ... state , validators , handleChange } ) } < /FormContextProvider>); } export default Form ;