import { FormFieldProps, FormValidator } from '../../components/silke-form'; import { getFieldContext } from '../../components/silke-form/form-context'; import { useCallback, useContext, useEffect, useRef, useState } from 'react'; const emptyValidator = () => undefined; export function useFormField, U = any>( props: T, validator: FormValidator = emptyValidator, ): Omit & { validate: () => void; onChange: (change: U) => void } { const { name, onError } = props; let { value, onChange } = props; const context = useContext(getFieldContext()); const valueRef = useRef(); const propsRef = useRef(); const validatorRef = useRef>(validator); validatorRef.current = validator; const isContextField = name && !onChange; const { register } = context; const getValue = useCallback( () => (name && context ? (context.valueRef.current[name] as U) : valueRef.current), [context.valueRef, valueRef], ); const getError = useCallback((): string | undefined => { const msg = validatorRef.current(propsRef.current as T, getValue()); if (msg) return msg; return ( propsRef.current?.validator?.(propsRef.current as T, getValue()) || validatorRef.current?.(propsRef.current as T, getValue()) ); }, [getValue, propsRef]); useEffect(() => { return register(name as string, { getError, getValue }); }, [name, getError, register, isContextField, propsRef, valueRef]); if (name && !onChange) { value = context.value[name]; onChange = (update: U) => { valueRef.current = update; context.onChange(name, update); }; } else if (!onChange) { onChange = () => console.log('Missing onChange'); } const error = props.error || context.errors[name as string]; propsRef.current = props; valueRef.current = value; // if (context?.valueRef.current && name) context.valueRef.current[name] = value; const validate = () => { // If context field then pass validate on to context // Else send error if error callback if (isContextField) context.validate(name as string); else if (onError) onError(getError()); }; const [touched, setTouched] = useState(false); useEffect(() => { validate(); }, [value]); return { ...props, value, error: props.error || touched ? error : undefined, validate, onChange, onBlur(e) { if (props.onBlur) props.onBlur(e); setTouched(true); validate(); }, onError, }; }