[React] 비제어 컴포넌트와 React-Hook-Form

장유진·2023년 7월 7일
0

React

목록 보기
1/2

비제어 컴포넌트와 React-Hook-Form

프로젝트를 구현할 때에는 서버로 사용자가 입력한 정보를 보내주는 기능이 필요합니다. 회원가입이나 파일 전송 등 다양한 기능에서 사용되는데요. 이 글에서는 React-Hook-Form 라이브러리를 소개하고 설명합니다.

제어 컴포넌트와 비제어 컴포넌트

  • 제어 컴포넌트(Controlled Component): React State에 의해 값이 관리되고, 변경되는 컴포넌트
  • 비제어 컴포넌트(Uncontrolled Component): 상태가 React State가 아닌 외부에서 관리되는 컴포넌트

제어 컴포넌트는 컴포넌트의 상태를 React에서 관리하기 때문에, 컴포넌트의 상태가 변경될 때마다 리렌더링이 발생합니다. 반면, 비제어 컴포넌트는 컴포넌트의 상태를 State에서 관리하지 않고 DOM으로 바로 접근(ref)하기 때문에 이벤트가 발생해도 화면의 리렌더링이 일어나지 않습니다.

제어 컴포넌트로 폼 구현하기

import { useState } from 'react';

const Form = () => {
    const [name, setName] = useState('');
		const [isError, setIsError] = useState(false)
    
    const handleSubmit = e => {
    	e.preventDefault();

			if(name === '') return setIsError(true)

      alert('API Request')
    };
    
    const onChangeNameHandler = e => {
    	e.preventDefault();
      console.log('NAME:', name);
    };
    
    return(
    	<form onSubmit={handleSubmit}>
            <input 
             	type="text"
                placeholder="Name"
                value={name}
                onChange={onChangeNameHandler}
            />
            <button>Enter Name</button>
						{isError && '이름을 빈 값으로 제출할 수 없습니다.'}
        </form>
    );
};

export default Form;

React에서는 일반적으로 제어 컴포넌트를 사용할 것을 권장하지만, 값이 바뀔 때마다 렌더링을 하기 때문에 불필요한 리렌더링 혹은 API 요청이 발생하기도 합니다. 때문에 폼의 Input 값을 onChange로 감시할 경우, Input이 많아지면 많아질수록 성능 이슈가 발생할 수 있습니다.

React-Hook-Form

React Hook Form은 React에서 폼을 만들 때 유용한 라이브러리입니다. React Hook Form은 비제어 컴포넌트를 활용하여 불필요한 리렌더링을 방지하여 직접 구현할 때보다 성능상 이점이 있고, 실시간 유효성 검사 등 다양한 기능을 제공합니다. 뿐만 아니라 가장 많은 사람들이 채택하고 있는 폼 라이브러리로 유저 풀이 넓으며 지속적으로 유지 보수가 이루어지고 있습니다. 타입스크립트를 기본으로 지원합니다.

https://react-hook-form.com/

설치

npm install react-hook-form

사용 예제와 메서드(Hook)

import { useForm, SubmitHandler } from "react-hook-form"

type Inputs = {
  example: string,
  exampleRequired: string
}

export default function App() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm<Inputs>()

  // onSubmit 이벤트에서 트리거되는 handleSubmit함수의 인자 data에는 register 함수로 등록된 모든 값이 들어옵니다.
  const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data)

  console.log(watch("example")) // input의 값 변화를 감지하여 값 출력

  return (
    /* "handleSubmit" 함수는 submit 이벤트가 발생하면 입력 필드의 값을 유효성을 검사한 후 "onSubmit" 함수를 호출합니다.*/
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* "register" 함수를 사용하여 입력 필드를 hook에 등록합니다 */}
      <input {...register("example")} />

      {/* "register" 함수 내에서 유효성 검사를 할 수 있습니다. */}
      <input {...register("exampleRequired", { required: true })} />
      {/* rules이 어긋나면 error 정보가 errors 객체에 저장됩니다 */}
      {errors.exampleRequired && <span>이 필드는 필수 입력입니다</span>}

      <input type="submit" />
    </form>
  )
}

useForm

  • React Hook Form을 초기화하고 Form 관리에 필요한 메소드와 속성을 반환하는 메인 함수입니다.
  • mode 속성을 사용해서 ****유효성 검사를 트리거하고 구성 요소를 다시 렌더링하는 방법을 결정할 수 있습니다.
  • 기본은 onSubmit으로, 버튼을 클릭할 때 유효성 검사를 실행합니다.
const { register, handleSubmit, errors } = useForm({
  mode: 'onSubmit' // or 'onChange', 'onBlur', 'onTouched'
});

register

  • React Hook Form으로 Input 요소를 등록하는 데 사용됩니다.
  • <input {...register('example')} />

watch / getValues

  • Input값을 감지하는 훅입니다.
  • watch는 해당 값에 따라 리렌더링을 발생시키지만, getValues는 리렌더링을 발생시키지 않는다는 차이가 있습니다.
  • watch('example')
  • getValues('example')

handleSubmit

  • 폼을 제출할 때 트리거 되는 훅입니다. 콜백 함수를 매개변수로, 폼 제출 시에 실행됩니다.
// 버튼을 클릭하면 handleSubmit의 매개변수를 콜백으로 실행합니다. 
const submitFunction = (data) => {
	// 유효성 검사...
	// API 요청...
}

<form onSubmit={handleSubmit(submitFunction)}>
   ...
	<button type="submit">Send</button>
</form>

setValue

  • Input 요소에 값을 입력해주는 훅입니다.
  • 외부 이벤트 또는 사용자 상호 작용을 기반으로 입력 값을 업데이트하는 데 사용할 수 있습니다.
  • 두 개의 인자를 갖습니다. 첫번째 인자로는 register에 등록한 input의 이름, 두번째는 입력해줄 값입니다.
  • setValue('message', 'default value')

errors

  • Input 요소의 유효성 검사 결과값을 나타내는 객체입니다.
  • 유효성 검사가 통과하면 null, 실패하면 string 값이 반환됩니다. 실패 시 반환되는 string 값은 유효성 검사에 등록한 메세지입니다.
  • errors.firstName && <span>{errors.firstName.message}</span>

setError

  • React Hook Form 으로 에러를 커스텀할 수 있습니다.
  • Input 요소의 입력값을 기반으로 특정 조건을 판별해야 할 때 사용할 수 있습니다.
import { useForm } from 'react-hook-form';

function Form() {
  const { register, handleSubmit, setErrors } = useForm();

  const onSubmit = (data) => {
		// 입력된 값을 가지고 비밀번호와 비밀번호 확인이 일치하는지 확인합니다
    if (data.password !== data.confirmPassword) {
      setErrors({ confirmPassword: 'Passwords do not match' });
      return;
    }

    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
			{/* 비밀번호와 비밀번호 확인의 유효성 검증 : true */}
      <input name="password" type="password" ref={register({ required: true })} />
      <input name="confirmPassword" type="password" ref={register({ required: true })} />
      <button type="submit">Submit</button>
    </form>
  );
}

이 외에도 React Hook Form은 수많은 기능을 지원합니다. 해당 페이지에서 확인해보세요!

https://react-hook-form.com/docs/useform

React Hook Form과 유효성 검사

Form 을 통해서 사용자에게 데이터를 입력받는다면 입력받은 데이터를 폼 전송 전에 검증해야 할 필요가 있습니다. React Hook Form의 가장 큰 매력점으로 유효성 검사를 위한 다양한 프로퍼티를 지원해준다는 것을 꼽고 싶습니다. 커스텀 오류가 필요하지 않은 경우 setError 가 아니라 form 태그 안에서 검증을 할 수 있습니다.

  • required - 필수 입력 검증
  • min - 최소값
  • max - 최대값
  • minLength - 최소 길이
  • maxLength -최대 길이
  • pattern - 정규식
  • validate - 커스텀 검증
import { useForm } from 'react-hook-form';

function MyForm() {
  const { register, handleSubmit, errors } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        name="name"
        ref={register({ required: 'This field is required' })}
      />
      {errors.firstName && <span>{errors.name.message}</span>}

      <input
        name="age"
        type="number"
        ref={register({
          min: { value: 18, message: 'Must be at least 18 years old' },
          max: { value: 100, message: 'Cannot exceed 100' }
        })}
      />
      {errors.age && <span>{errors.age.message}</span>}

      <input
        name="password"
        type="password"
        ref={register({ minLength: { value: 6, message: 'Password must be at least 6 characters long' } })}
      />
      {errors.password && <span>{errors.password.message}</span>}

      <input
        name="email"
        ref={register({
          required: 'Email is required',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: 'Invalid email address'
          }
        })}
      />
      {errors.email && <span>{errors.email.message}</span>}

      <input
        name="username"
        ref={register({ validate: value => value === 'admin' || 'Invalid username' })}
      />
      {errors.username && <span>{errors.username.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

React Hook Form과 Default Value 설정

useForm 을 사용하는 경우에는 반드시 defaultValue 를 넣어줘야 useForm 을 통해 관리되는 필드들의 초기값이 undefined 로 관리되지 않습니다.



Refercnce
⓵ React Hook Form (Official Document) https://react-hook-form.com/
⓶ React controlled vs. uncontrolled components
https://velog.io/@kskim625/React-controlled-vs.-uncontrolled-components
⓷ React 제어 컴포넌트 vs 비제어 컴포넌트
https://itprogramming119.tistory.com/entry/React-제어-컴포넌트-VS-비제어-컴포넌트
⓸ react-hook-form을 이용해 효과적으로 폼 관리하기
https://tech.osci.kr/introduce-react-hook-form/
⓹ 깔끔한 폼 개발과 정시퇴근을 위하여 react-hook-form
https://dealicious-inc.github.io/2022/07/25/ss-studio.html
⓺ React Hook Form을 활용한 회원가입 Validation 구현
https://velog.io/@sweetpumpkin/React-hook-form을-이용한-Form-Validation

0개의 댓글