Custom Form (react-hook-form)

Ryan Cho·2024년 12월 4일
1

커스텀 폼 컴포넌트

react-hook-form을 이용한 커스텀 폼 컴포넌트, RHForm이라고 만들어보자.

유효성 검사에 관한건 hook-form을 이용하지 않고, zod를 사용한 schema기반으로 만든다.
(훅폼의 코어인 useForm에 resolver프로퍼티에 zodResolver를 이용해 연결)

FormProvider를 통해 react-hook-form의 컨텍스트를 제공해 자식 컴포넌트에서 폼 상태에 접근 가능.

import { DevTool } from "@hookform/devtools";
import { zodResolver } from "@hookform/resolvers/zod";
import { PropsWithChildren } from "react";
import {
  DefaultValues,
  FieldValues,
  FormProvider,
  SubmitHandler,
  useForm,
} from "react-hook-form";
import { ZodSchema } from "zod";

//실행 시점에 타입을 정하는게 좋습니다!

type RHFormProps<T> = {
  //2) 몰라 여기서 받아오셈, 근데 이건 어디서 받아옴?
  schema: ZodSchema<T>; // 1) T가 뭔데?
  defaultValues?: DefaultValues<T>;
};
// 3) 몰라 props로 넘길때 타입 알아서 맞춰서 주겠지, 다 돌려서 쓰셈
function RHForm<T extends FieldValues>({
  children,
  schema,
  defaultValues,
  showDevTools = import.meta.env.MODE === 'development',
  onSubmit,
  onError,
  validateOnload = false,
  mode = 'all'
}: PropsWithChildren<RHFormProps<T>>) {
  const methods = useForm<T>({
    defaultValues: defaultValues,
    resolver: zodResolver(schema),
    mode
  });
  
  useEffect(() => {
  	if(validateOnload){
    	methods.trigger()
    }
  },[methods, methods.trigger, validateOnload])
  
  return (
    <FormProvider {...methods}>
      <form onSubmit={methods.handleSubmit(onSubmit, onError)}>{children}</form>
      {showDevTools && <DevTool control={methods.control} />}
    </FormProvider>
  );
}

export default RHForm;

이 커스텀 폼에서 가장 중요한건 타입정의, 즉 제네릭을 어떻게 잘 활용했냐는것이다.

컴포넌트의 실행 시점에 타입을 정하는게 가장 좋은 관습이고 타입스크립트에서 타입 추론능력을 잘 활용할 수 있다.

T라고 명시한 제네릭을 순서대로(1,2,3) 따라가다보면 즉 타입결정권을 추상화하여 타입의 유연성을 강화해 재사용성이 높고, 안정성있는 커스텀 폼으로 사용할 수 있다.

적용

...

return (
	<RHForm<TestType> schema={schema}>
    	...
        <Button type='submit'/>
 		<Button type='reset'/>
    </RHForm>
)

엄격하게 타입처리를 하려면 이와 같이 조금 어색해보일수 있는 형태로 컴포넌트를 사용할 수 있다.

물론 타입스크립트의 타입 추론 능력으로 단순히 제네릭 없이도 동일하게 동작할것이지만,
위 방식처럼 제네릭으로 타입을 넘기면 더 완벽한 코드를 만들 수 있다.

profile
From frontend to fullstack

0개의 댓글