react-hook-form을 조금 더 재사용성 있게 사용하기

HappyFrog·2023년 9월 26일
0

react-hook-form

목록 보기
1/3
post-thumbnail

이전글에서 이어지는 내용입니다.

개요

기존의 react-hook-formuseController를 활용하여 하나의 프로젝트에서 보다 재사용성을 높혀서 활용할 수 있었지만, UI 라이브러리를 antd로 변경하여 활용하면서 기존 방식의 문제점들 발견 하게 되었다.
바로 다른 Input 컴포넌트(UI 라이브러리 혹은 내 커스텀 input등)을 활용하려 할때 마다 새롭게 useController를 포함한 컴포넌트를 생성해야 한다는 점이였다. 물론 한 프로젝트에서 다른 Input 컴포넌트들을 조합해서 쓸일은 많지 않겠지만 규모가 작은 프로젝트(혹은 사이드 프로젝트)들을 개발 할때 이 점이 단점으로 다가오게 되었다.
더군다나 최근 MSA(MicroService Architecture)모노레포를 채택하는 일이 많아지면서 이러한 단점이 크게 다가오게 되어 개선 작업을 하게 되었다.

기존의 문제점

기존의 구조는 아래와 같았다

// react-hook-form import는 생략....
import { Input } from "antd"; // 사용할 Input 컴포넌트 import
// type import 생략....

const MyInput = <
      T extends FieldValues, 
      K extends FieldPath<T>
>({ controllerProps, ...props }
  :{ controllerProps: UseControllerProps<T,K> & InputProps }) => {
 	const { field } = useController({...controllerProps});
        
    return (
    	<Input {...field} />
    )
 }

즉, MyInput 컴포넌트에 사용한 Input을 import 함으로써 사용할 Input외에 다른 컴포넌트를 사용하고 싶을 경우 동일한 방식의(사실상 import만 다른) 다른 컴포넌트를 하나 더 생성 하여야 하였다.

개선

즉, 위의 문제점을 개선하기 위해서 Input 컴포넌트를 prop으로 전달할 필요성이 있어 다음 코드와 같이 구현하게 되었다.

import React, { ComponentProps, ElementType } from "react";
import { useController } from "react-hook-form";
import type { UseControllerProps, FieldPath, FieldValues} from "react-hook-form";

interface ControlledInputProps<
  T extends FieldValues,
  K extends FieldPath<T>,
  U extends ElementType
> {
  controllerProps: UseControllerProps<T, K>;
  as: U;
  asProps?: ComponentProps<U>;
}

const ControlledInput = <
  T extends FieldValues = FieldValues,
  K extends FieldPath<T> = FieldPath<T>,
  U extends ElementType = "div"
>(
  props: ControlledInputProps<T, K, U>
) => {
  // as => input component, asProps => input component props
  const { controllerProps, as, asProps } = props;
  const { field } = useController({ ...controllerProps });

  return React.createElement(as, { ...field, ...asProps });
};

export default ControlledInput;

위의 코드와는 다르게 as prop을 통해 input 컴포넌트를 받아 활용하였고, asProps를 통해서 해당하는 컴포넌트의 props를 전달 받았다. 또한 ComponentProps를 활용해 as를 통해 전달한 input 컴포넌트의 props를 추론할 수 있도록 사용하였다.

사용 및 결론

import { Input } from "antd";
import { TextField } from "@mui/material";
// 만든 컴포넌트 import
import MyInput from "@/somewhere";

// form의 type
type FormType = {
	password?: string;
  	id?: string;
};

const SomeFormComponent = () => {
	const { control, handleSubmit } = useForm<FormType>({
    	defaultValues: {
        	id: ""
        }
    });
  
  	const onSubmit = (data: FormType) => { console.log(data); };
  	
  	return (
    	<form onSubmit={handleSubmit(onSubmit)}>
        	<MyInput 
              controllerProps={{ control, name: "id" }}
              as={TextField}
              asProps={{ 
             	//... 원하는 props들
              }}
            />
        	<MyInput 
              controllerProps={{ control, name: "password" }}
              as={Input.password}
              asProps={{ 
             	//... 원하는 props들
              }}
            />
      	</form>
    )
};

export default SomeFormComponent;

즉 위와 같은 구조로 as를 통해 Input 컴포넌트를 전달하여 이전보다 활용성을 높일 수 있게 되었다.
물론 개요에서 언급 했던 것처럼 단일 프로젝트 보다는 MSA모노레포 구조에서 보다 더 잘 활용 할 수 있을 것이라 생각한다.

profile
성장하고 싶은 긍정 개구리

0개의 댓글