UNPKG

pocket-hook-form

Version:

pocket-store base hook form

103 lines (94 loc) 2.65 kB
// hooks/useController.ts import {useCallback, useEffect, useMemo, useRef} from 'react'; import type { FormStoreControl, FormValue, FieldRef, UseControllerReturn, FormState, FieldRules, } from '../models/type'; import getEventValue from '../utils/getEventValue'; import convertToArray from '../utils/convertToArray'; import {Store, useStore} from 'pocket-state'; export function useController<T extends FormValue, K extends keyof T>( control: FormStoreControl<T>, name: K, opts?: { next?: keyof T; rules?: FieldRules<T[K], T>; }, ): UseControllerReturn<T, K> { const store = (control as any)._store as Store<FormState<T>>; const {value, errors, touched, isDirty} = useStore(store, s => ({ value: s.values[name], errors: s.errors?.[name], touched: !!s.touched[name], isDirty: !!s.dirty[name], })); const ref = useRef<FieldRef>(null); useEffect(() => { if (!opts?.rules) return; const current = (control.getStoreOptions().rules ?? {}) as Partial<{ [P in keyof T]: FieldRules<T[P], T>; }>; const prevField = current[name] as FieldRules<T[K], T> | undefined; const mergedField: FieldRules<T[K], T> = { ...(prevField ?? {}), ...(opts.rules ?? {}), }; control.updateOptions({ rules: { ...current, [name]: mergedField, } as Partial<{[P in keyof T]: FieldRules<T[P], T>}>, }); return () => { const latestRules = (control.getStoreOptions().rules ?? {}) as Partial<{ [P in keyof T]: FieldRules<T[P], T>; }>; if (latestRules[name]) { delete latestRules[name]; control.updateOptions({ rules: latestRules, }); } }; }, [control, name, opts?.rules]); useEffect(() => { control.addRef(name, ref.current); return () => control.removeRef(name); }, [control, name]); const onChange = useCallback( (e: any) => { const v = getEventValue(e); control.setValue(name, v); }, [control, name], ); const onBlur = useCallback(() => { const {mode} = control.getStoreOptions(); control.setTouched?.(name, true); if (mode === 'onBlur') { (control as any).validateField?.(name); } }, [control, name]); const onSubmitEditing = useCallback(() => { if (opts?.next) control.focus(opts.next); }, [control, opts?.next]); return useMemo( () => ({ value, onChange, onBlur, ref, onSubmitEditing, fieldState: { touched, isDirty, errors: errors && convertToArray(errors), }, }), [value, ref, touched, isDirty, errors], ); }