프레임워크/React(NextJs)

[React] react-hook-form(feat. typescript)으로 Input 컴포넌트 만들어보기

릴리코더 2022. 9. 6. 21:41

이 글은 프로젝트의 파일 구조와 디자인패턴을 고민하면서 시도했던 방법을 작성해두기 위한 글입니다.

해당 코드는 현재 사용하는 방식일 수도, 아닐 수도 있습니다.

이런식으로 코드를 짜봤구나. 정도의 예시로 봐주시면 감사하겠습니다.

 

 

 

사용한 라이브러리 : react-hook-form

 

 

./src/components/forms/Form.tsx

import * as React from 'react';

interface onSubmitProps {
  onSubmit: React.FormEventHandler;
  children: React.ReactNode;
}

const Form = ({ onSubmit, children }: onSubmitProps) => {
  return <form onSubmit={onSubmit}>{children}</form>;
};

export default Form;

 

 

./src/components/forms/InputText.tsx

import * as React from 'react';
import { ChangeHandler } from 'react-hook-form';

interface FormRegister {
  name: string;
  ref: React.Ref<any>;
  onChange?: ChangeHandler;
  onBlur?: ChangeHandler;
}

export interface InputTextProps {
  type: 'text' | 'email' | 'password';
  defaultValue?: string;
  placeholder?: string;
  formRegister?: FormRegister;
}

const InputText = ({ type, defaultValue, placeholder, formRegister }: InputTextProps) => {
  return (
    <input type={type} defaultValue={defaultValue} placeholder={placeholder} {...formRegister} />
  );
};

export default InputText;

 

./src/components/forms/SubmitButton.tsx

interface SubmitButtonProps {
  value: string;
}

const SubmitButton = ({ value }: SubmitButtonProps) => {
  return <input type="submit" value={value} />;
};

export default SubmitButton;

 

 

./src/components/list/InputCard.tsx

import { useForm } from 'react-hook-form';

import Form from '../forms/Form';
import InputText from '../forms/InputText';
import SubmitButton from '../forms/SubmitButton';

export interface FormType {
  content: string;
}

const InputCard = ({ content }: FormType) => {
  const { handleSubmit, register } = useForm<FormType>();
  
  const submitHandler = handleSubmit((data) => console.log(data));

  return (
    <Form onSubmit={submitHandler}>
      <InputText
        type="text"
        formRegister={register('content')}
        defaultValue={content}
        placeholder="데이터를 입력해주세요"
      />
      <SubmitButton value="저장" />
    </Form>
  );
};

export default InputCard;

 

일단 type="text"를 기준으로 만든 컴포넌트인데, input 태그에는 타입이 많다.

그래서 그중에서 text, email, password, mobile, ... 등은 같은 컴포넌트로 묶을 수 있을 것 같고,

number는 조금 더 생각해봐야할 것 같고.. (onSubmit했을 때 string값으로 뽑아낼 수 있긴한데, input 모양이 조금 달라서..)

file이나 checkbox, radio는 너무 다르니까 각각 컴포넌트를 만들어야할 것 같다.

 

 

음.. 아니면 아래 포스팅 내용에 있는 예시 처럼 컴포넌트를 객체화 시켜서 관리하면 편할지도?

https://javascript.plainenglish.io/5-advanced-react-patterns-a6b7624267a6

 

<Counter.Decrement />
<Counter.Label />

 

이렇게 예시 있는 것 처럼 아래와 같이 만드는 것도 고려해보자

<Input.text />
<Input.file />
<Input.Radio />

 

 

그리고 추가적으로 InputCard 는 좀 더 분리가 필요함.

submitHandler이 저렇게 들어가면 view와 비즈니스 로직이 구분이 안된 것.

handleSubmit안에 있는 function을 props로 넘겨받는 쪽으로 수정해서 사용하기 🙌

onSubmit에 동작할 함수는 이 컴포넌트를 사용하는 페이지에서 선언하는 걸로!

 

 

 

갑자기 든 생각인데, input도 HTML태그 중에 컴포넌트화 된거라고 할 수 있으려나..? ㅎ

그렇다면 하나의 태그에 너무 많은 기능을 넣은 걸지도.. select box처럼 따로 뺐어야 하는게 아닐까?