기록

(TypeScript) react-hook-form에서 form과 input을 분리하는 법

als982001 2023. 6. 6. 14:28

 form과 input을 분리하고 싶었지만, 어떻게 하는지 몰라서 헤메고 있던 중, 좋은 글을 발견했다. 간단히 이야기하자면 form 안에서 input을 쓸 때는 register를 이용하면 되고, 분리하고 싶을 때는 useController를 이용하면 된다는 것 같다. 다음은 간단한 Login form이다.

// Login.tsx
import React from "react";
import styled from "styled-components";
import Logo from "../Components/Global/Logo";
import MemberInput from "../Components/Global/Inputs/MemberInput";
import { FieldValues, SubmitHandler, useForm } from "react-hook-form";
import { handleLogin } from "../utils/MemberFunctions";
import { displayCenter, displayStartA } from "../styles/displays";

const Wrapper = styled.div`
	// 생략
`;

const Container = styled.form`
	// 생략
`;

const Inputs = styled.div`
	// 생략
`;

const Button = styled.button`
	// 생략
`;

interface FormValues {
  userId: string;
  password: string;
}

export default function Login() {
  const { handleSubmit, control } = useForm<FormValues>({
    defaultValues: {
      userId: "",
      password: "",
    },
    mode: "onChange",
  });

  const handleStartLogin = async (data: FormValues) => {
    console.log(data);
  };

  return (
    <Wrapper>
      <Container onSubmit={handleSubmit(handleStartLogin)}>
        <Logo logoSize={"100px"} />
        <Inputs>
          <MemberInput
            control={control}
            name="userId"
            rules={{ required: true }}
            width="100%"
            height="40px"
            placeholder="ID를 입력하세요."
          />
          <MemberInput
            control={control}
            name="password"
            rules={{ required: true }}
            width="100%"
            height="40px"
            placeholder="비밀번호를 입력하세요."
          />
        </Inputs>
        <Button>로그인</Button>
      </Container>
    </Wrapper>
  );
}

 그리고 아래는 분리한 Input이다. (위의 코드에는 MemberInput이라는 이름인 것을 확인할 수 있다.)

import React from "react";
import {
  Control,
  RegisterOptions,
  useController,
  UseControllerProps,
} from "react-hook-form";
import styled from "styled-components";

interface FormValues {
  userId: string;
  password: string;
}

interface StyleType {
  width: string;
  height: string;
  placeholder: string;
}

const Input = styled.input<StyleType>`
	// 생략
`;

function MemberInput(props: UseControllerProps<FormValues> & StyleType) {
  const { field, fieldState } = useController(props);

  return (
    <Input
      {...field}
      width={props.width}
      height={props.height}
      placeholder={props.placeholder}
    />
  );
}

export default MemberInput;
  1. 기존이라면 useForm의 register에 등록했을 name들의 타입을 Form과 Input 부분 모두 미리 지정한다. 위의 코드에서는 FormValues라는 이름으로 저장되어 있다.
  2. (Form) useForm의 defaultValue를 통해 각 name들의 초기값을 설정하면서 control을 구조 분해 할당으로 불러온다.
  3. (Form) 각 Input마다 control={control}, name, rules 등을 props로 준다.
  4. (Input) useController에서 field를 구조 분해 할당으로 가져온다.
  5. (Input) input에 field를 위의 코드처럼 작성한다.

 

 그동안 react-hook-form을 쓰면서 useController는 몰랐기에 이를 적용하는 데 너무 오랜 시간이 걸렸었다. 그래도 성공하니까 뿌듯하다. useController처럼 여기서 처음 보는 것들이 많이 있는데, 이것들에 대해 잠시 정리를 해야 겠다. 요즘 유행하는 chatGPT의 힘을 빌려 개념 정리를 하였다.

 

// useController 간단한 적용 예시
const { field, fieldState } = useController({
  name: 'fieldName', // 입력 요소의 이름
  control: form.control, // useForm 훅에서 반환된 control 객체
  rules: { required: true }, // 입력 요소에 적용할 유효성 검사 규칙
});
  • useController
    • 컨트롤러를 생성하여 폼의 입력 요소와 상태를 관리하는 훅
    • 입력 요소와 해당 입력 요소의 상태를 쉽게 연결할 수 있음
    • 폼의 입력 요소를 직접 관리할 필요 없이 useForm에서 반환된 control 객체와 연결하여 간단하게 폼 상태를 관리할 수 있으며 폼의 유효성 검사, 상태 추적, 에러 처리 등을 효율적으로 할 수 있음
    • field와 fieldState는 useController에서 반환되는 객체
      • field: 입력 요소와 연결된 필드 속성을 나타내는 객체. 이 객체를 입력 요소에 전달하여 react-hook-form이 제공하는 다양한 기능을 활용 가능. 예를 들어, field 객체를 input 요소의 props로 전달하면 value, onChange, onBlur 등의 이벤트 핸들러가 자동으로 설정됨
      • fieldState: 입력 요소의 상태 정보를 나타내는 객체. error와 isDirty, isValid 등의 속성을 포함. 이 객체를 사용하여 입력 요소의 상태에 따라 동적인 스타일이나 에러 메시지를 표시 가능.
  • UseControllerProps
    • useController 함수에 전달되는 매개변수의 타입을 나타내는 인터페이스. 이 인터페이스는 name, control, rules 등의 필드를 정의하며, 입력 요소의 타입과 필수 여부 등을 지정할 수 있음.
    • 위의 예시 코드에서 MemberInput 컴포넌트에서 UseControllerProps를 사용하여 입력 요소의 타입과 폼 관련 설정을 전달하고, useController를 사용하여 입력 요소와 상태를 관리하고 있음.

 

 


참고한 글 (React-hook-form - useController)

https://velog.io/@hwisaac/React-hook-form-useController