티스토리 뷰

1. React-hook-form에서 Props Drilling 방지

:: 제어 or 비제어에서 useForm 사용 시, 계층 구조가 만들어짐

:: 구조가 커질수록 계층별로 퍼져 각 계층의 자식 인풋을 연결하면, Props Drilling 발생

:: <FormProvider> + useFormContext() 사용

  • <FormProvider>  == <Context.Provider>
  • useFormContext()  == useContext()

1.1  <FormProvider>

:: <form>으로 모든 인풋을 관리할 기반 폼 생성

:: useForm()을 정의하는 곳에서 <form>과 함께 정의

:: useForm()엣 반환된 register, handleSubmit, formState 등을 하위 컴포넌트가 접근할 수 있도록 함

function RegistrationForm() {
  const methods = useForm()
  const { register, reset } = methods

  return (
    <FormProvider {...methods}>
      <form onSubmit={methods.handleSubmit((data) => console.log(JSON.stringify(data)))}>
        {/* ... 수많은 인풋들, 꼭 직계자식이 아니라 자식의 자식의 자식이여도 무방하다. */}
      </form>
    </FormProvider>
  )
}

1.2 useFormContext()

:: 인풋 정의 시, 앞선 <form>에 연결

:: useFormContext()와 useForm이 반환하는 함수와 객체는 동일

const methods = useForm()
const methods = useFormContext()

 

적용 예시

function UsernameInput() {
  const methods = useFormContext()
  const { register, formState: { errors } } = methods

  return (
    <div>
      <label htmlFor='username'>Username : </label>
      <input
        id='username'
        {...register('username', {
          required: true,
          maxLength: {
            value: 10,
            message: '아이디는 10자 이상이 될 수 없습니다',
          },
        })}
      />
      <div>
        {errors?.username?.type === 'required' && '아이디는 필수 입력항목 입니다'}
        {errors?.username?.message}
      </div>
    </div>
  )
}

1.3 <FormProvider> + useFormContext()  적용 예시

더보기

 1. <FormProvider>를 통한 form 범위 지정

function RegistrationForm() {
  const methods = useForm()
  const { register, reset } = methods

  return (
    <FormProvider {...methods}>
      <h3 style={{ margin: 0 }}>유저 추가하기</h3>
      <form className='form-wrapper' onSubmit={methods.handleSubmit((data) => console.log(JSON.stringify(data)))}>
        <UsernameInput />
        <PasswordGroupInput />
        <SubmitButton />
        <ErrorLogButton />
      </form>
    </FormProvider>
  )
}

2. 인풋 컴포넌트 분리 - useFormContext를 통해 부분적 전역 변수로 값을 공유 가능

function UsernameInput() {
  const {
    register,
    formState: { errors }
  } = useFormContext();

  return (
    <>
      <div>
        <label className='input-label' htmlFor='username'>유저이름 : </label>
        <span className='input-area'>
          <input
            id='username'
            className={clsx('input', errors?.username && 'error-border')}
            {...register('username', {
              required: true,
              maxLength: {
                value: 10,
                message: '아이디는 10자 이상이 될 수 없습니다',
              },
            })}
          />
        </span>
      </div>
      <div className={errors?.username ? 'error-text' : ''}>
        {errors?.username?.type === 'required' && '아이디는 필수 입력항목 입니다'}
        {errors?.username?.message}
      </div>
    </>
  )
}

3. useFormContext()를 통한 유효성 확인 

function SubmitButton() {
  return <button type='submit'>저장하기</button>
}

function ErrorLogButton() {
  const { formState: { errors } } = useFormContext()

  return <button onClick={() => console.log(errors)}>확인하기</button>
}

function App() {

  return (
    <RegistrationForm />
  )
}

2.  동적 폼 제공 방법

2.1 useFieldArray()

:: 고정된 정적 폼이 아니라 동적 폼 사용 시

:: 배열 값인 fielfs + 배열 요소 추가/삭제 등을 위한 append, remove 등의 함수 제공

- 구성

const { fields, 
		append,
        prepend,
        remove,
        swap, 
        move, 
        insert } = useFieldArray({ control, name: "name" });

 

- Ex

더보기
import React from 'react';
import { useForm, useFieldArray, Controller, FormProvider } from 'react-hook-form';

function DynamicForm() {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      users: [{ name: '', email: '' }]  // 배열 형태로 초기값 설정
    }
  });

  const { fields, append, remove } = useFieldArray({
    control,
    name: "users",  // 배열의 이름을 지정
  });

  const onSubmit = data => {
    console.log(data);  // 제출된 폼 데이터 출력
  };

  return (
    <FormProvider {...{ control }}>
      <form onSubmit={handleSubmit(onSubmit)}>
        <h3>동적 사용자 추가</h3>
        
        {fields.map((item, index) => (
          <div key={item.id} style={{ marginBottom: "10px" }}>
            <Controller
              control={control}
              name={`users[${index}].name`}
              render={({ field }) => <input {...field} placeholder="이름" />}
            />
            <Controller
              control={control}
              name={`users[${index}].email`}
              render={({ field }) => <input {...field} placeholder="이메일" />}
            />
            <button type="button" onClick={() => remove(index)}>
              삭제
            </button>
          </div>
        ))}

        <button type="button" onClick={() => append({ name: "", email: "" })}>
          사용자 추가
        </button>

        <button type="submit">제출</button>
      </form>
    </FormProvider>
  );
}

 

- Ex  2

function SpecialtyArrayInput() {
  const { control, register } = useFormContext();
  const { fields, append, remove } = useFieldArray({ control, name: "specialty" });

  return (
    <>
      {fields.map((field, index) => (
        <input key={field.id} {...register(`specialty.${index}`)} />
      ))}
      <span>
        <button type='button' onClick={() => append('')}>추가</button>
        <button type='button' onClick={() => remove(fields.length - 1)}>삭제</button>
      </span>
    </>
  );
}

참고

ASAC 수업자료

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함