본문으로 바로가기

[React] react-hook-form 배열 다루기 (useFieldArray)

react-hook-form에서 배열을 다룰 때는 useFieldArray hook을 사용하면 된다. 그러나 이 hook을 사용할 땐 주의할 점이 있는데 우선 안되는 코드를 살펴보고 잘 되는 코드를 확인해보자. (참고 : 이전 게시글에서 소개한 초기값, 출력과 같은 부분 설명은 제외함.)

수정 전 코드

import { Controller, useFieldArray, useForm } from "react-hook-form";

// # 초기값
const initValue = {
  numbers: []
};

const Basic = () => {
  const { control, handleSubmit } = useForm({
    mode: "onSubmit",
    defaultValues: initValue
  });

  // #1 useFieldArray 값
  const { fields, append } = useFieldArray({
    control: control,
    name: "numbers"
  });

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* #2 fields.map 그리기 */}
      {fields.map((item, index) => {
        return (
          <div key={index}>
            <Controller
              control={control}
              // #3 name
              name={`numbers[${index}]`}
              defaultValue=""
              render={({ field }) => (
                <input margin="dense" variant="outlined" {...field} />
              )}
            />
          </div>
        );
      })}
      <div>
        <button
          onClick={() => {
            // #4 배열 요소 추가하기
            append("");
          }}
        >
          추가
        </button>
        {/* #5 값 확인하기 */}
        <button onClick={() => console.log("# fields", fields)}>
          값 확인하기
        </button>
      </div>
      <input type="submit" />
    </form>
  );
};

export default Basic;

#1 useFieldArray 값

useFieldArray의 값은 fields이다. 즉 배열 값이 fields로 사용이 된다. 그리고 append 라는 함수가 있는데, 배열에 원소를 추가할 때 사용이 된다. 추가적인 함수로는 prepend, remove, swap, move, insert가 있다. (홈페이지 참고 : 이동하기)

#2 fields로 컴포넌트 그리기

fields가 배열 값이니 map을 통해 배열을 반복하면서 안의 원소 값으로 컴포넌트를 그리는 코드이다.

#3 name

name은 이전 게시글에서 값에 접근할 수 있도록 적어주면 된다고 했다. 배열 원소에 접근하는 방법으로 name을 적어주었다.

#4 배열 원소 추가하가

useFieldArray에서 원소를 추가하는 방법은 arr.push(원소)가 아닌 append(원소)이다.

#5 값 확인하기

초기 값이 [] 빈 배열이니 append를 하게 되면 [""]로 될 것 같다. 아래 결과 값을 확인해보자.

그러나 우리의 예상과 다르게 너무 이상한 값이 들어가있다. 왜 그럴까? 필자도 정확한 이유는 모르겠지만, 공식 홈페이지를 참고해보면 사용법이 잘못되었음을 알 수 있다.

설명을 보면 flat field array를 지원하지 않는다고 되어 있는데, 아마 flat field array가 그냥 숫자 배열, 문자열 배열 등을 뜻하는 것 같다. 그럼 어떻게하면 사용할 수 있을까? 아래 수정 후 코드를 확인해보자.

수정 후 코드

import { Controller, useFieldArray, useForm } from "react-hook-form";

// # 초기값
const initValue = {
  numbers: []
};

const Basic = () => {
  const { control, handleSubmit } = useForm({
    mode: "onSubmit",
    defaultValues: initValue
  });

  // #1 useFieldArray 값
  const { fields, append } = useFieldArray({
    control: control,
    name: "numbers"
  });

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* #2 fields.map 그리기 */}
      {fields.map((item, index) => {
        return (
          <div key={index}>
            <Controller
              control={control}
              // #3 name
              name={`numbers[${index}].value`}
              defaultValue=""
              render={({ field }) => (
                <input margin="dense" variant="outlined" {...field} />
              )}
            />
          </div>
        );
      })}
      <div>
        <button
          onClick={() => {
            // #4 배열 요소 추가하기
            append({ value: "" });
          }}
        >
          추가
        </button>
        {/* #5 값 확인하기 */}
        <button onClick={() => console.log("# fields", fields)}>
          값 확인하기
        </button>
      </div>
      <input type="submit" />
    </form>
  );
};

export default Basic;

사실 크게 변경된 곳은 없다. #3, #4만 변경해주었다. 왜 위와 같은 코드로 변경을 하면 잘 동작할까? 이유는 간단하다. flat field가 안되니 object로 변경해준 것이다. 즉 기존에는 const numbers = [1, 2, 3]이 었다면 지금은 const numbers = [{ value : 1 }, { value : 2 }, { value : 3 }] 인 것이다.

 

그러나 위와 같은 경우, 우리가 원하는 실제 numbers가 [1,2,3] 인 경우 const result = numbers.map(item => item.value); 로 빼내야 하는데 이게 맞는 방법인가 싶다. 결론적으로 const numbers = [1,2,3,4] 와 같은 경우는 useFieldArray로는 사용하지 못 하는건지, 방법을 모르는 건지 궁금하다. 혹시 아시는 분 있으면 댓글로 가르쳐주시면 감사하겠습니다.

마지막

해당 내용은 틀릴 수도 있다는 것을 감안하여 봐주세요. 틀린 내용 및 오탈자 수정 요청 환영입니다.

반응형