task-managment / src / hooks / useFormHandler.ts
useFormHandler.ts
Raw
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useMemo, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { z, ZodSchema } from "zod";

const useFormHandler =<T extends ZodSchema> (formSchema:T,defaultValues:z.infer<T>) => {
    type SchemaType = z.infer<T>
    const [isInputChanged,setIsInputChanged]=useState(false)
    const [addValues,setAddValues]=useState<Partial<z.infer<T>>>({})
    const form = useForm<z.infer<typeof formSchema>>({
      resolver: zodResolver(formSchema),
      defaultValues
    });
    const dirtyValues = form.formState.dirtyFields
    const isValuesSet=useRef(false)

    const getChangedFields=(values:Partial<SchemaType>)=>{
      const changedFields = Object.keys(dirtyValues).reduce<Partial<SchemaType>>(
        (acc, key) => {
          const typedKey = key as keyof SchemaType;
          const selectedValue = values[typedKey];
          if (selectedValue !== undefined) {
            acc[typedKey] = selectedValue as any; 
          }
          return acc;
        },
        {}
      );
      return changedFields
    }

    useEffect(()=>{
      if(isValuesSet.current)return
      const valuesToSet=Object.entries(addValues)
      if(!valuesToSet.length)return
      valuesToSet.map(([key,value])=>{
        form.setValue(key as any,value )
      })
      isValuesSet.current=true
    },[addValues,form])

    useEffect(() => {
        const subscription = form.watch((value, { name, type }) =>{
          if(type === 'change' && !isInputChanged){
            setIsInputChanged(true)
          } 
         }   
        )
        return () => subscription.unsubscribe()
      }, [form.watch,form,isInputChanged])

      const setFormValues = (values:Partial<z.infer<T>>)=>{
        setAddValues(values)
      }

      const isChanged = useMemo(()=>{
        return Object.values(getChangedFields(form.getValues()))?.length ? true : false
      },[form.getValues(),getChangedFields])
      
    return {
        form,
        isInputChanged:isChanged,
        setIsInputChanged,
        getChangedFields,
        setFormValues
    }

}

export default useFormHandler