React-hook-form 다차원 배열 관리하기

taeyooooon·2023년 6월 13일
1
post-thumbnail

React-hook-form 선택 이유❓

이력서 작성 사이트 프로젝트를 작업하던중 많으면 수십개의 input 관리를 효율적으로 하기위한 방법을 탐구하던 중 React-hook-form(이하 RHF)에 대해 알아보았다. RHF은 기본적으로 비제어 컴포넌트 방식으로 구현되어있어서 사용자가 input value를 수정할때 불필요한 렌더링 이슈를 해결 할 수 있다.

사용 기술 🛠

Next.js 13 Appdir,Typescript, Material UI

RHF의 튜토리얼 대부분에서 다차원 배열과 UI 라이브러리 컴포넌트를 동시에 제어하는 방법을 찾기가 힘들었습니다..

구현 목표

다차원 배열과 UI컴포넌트를 다루면서 동시에 실시간으로 Form value를 확인 할 수 있게끔 구현

제어할 데이터 형식

export const INIT_DATA: Data = {
  firstArray: [
    {
      first: '',
      secondArray: [
        {
          second: '',
          thirdArray: [
            {
              third: '',
            },
          ],
        },
      ],
    },
  ],
};

Edit할 컴포넌트에는 useForm의 control을 View할 컴포넌트에는 watch를 props로 보내준다.

'use client';

export default function Home() {
  const { handleSubmit, control, watch } = useForm({
    mode: 'onSubmit',
    defaultValues: { ...INIT_DATA },
  });
  const formData = watch();

  return (
    <>
      <Form
        handleSubmit={handleSubmit}
        control={control}
      />
      <View formData={formData} />
    </>
  );
}

배열을 관리하고자 하는 컴포넌트에서 useFieldArray를 생성한 뒤 fields를 이용해 map을 돌려줍니다
이때 주의해야하는게 평소에 key값에 다른 값을 넣는게 아니라 RHF이 자동으로 생성해주는 유니크 값을 사용해야합니다.

그리고 append, prepend 등등 로직을 수행할 때 default value를 작성해줘야합니다.

import { Control, useFieldArray } from 'react-hook-form';

interface Props {
  control: Control<Data>;
}

const FirstArrayForm = ({ control }: Props) => {
  const { fields, append } = useFieldArray({
    control,
    name: 'firstArray',  // 컨트롤하고자 하는 배열의 key값
  });

  const onAppend = () => {
    append({
      first: '',
      secondArray: [
        {
          second: '',
          thirdArray: [
            {
              third: '',
            },
          ],
        },
      ],
    });
  };

  return (
      <ul>
        {fields.map((field, index) => {
          return (
            <li key={field.id}>  // 유니크 key값
              <FirstArray
                control={control}
                firstIndex={index}
                resetField={resetField}
              />
            </li>
          );
        })}
        <button type='button' onClick={onAppend}>
          Append FirstArray
        </button>
      </ul>
  );
};
export default FirstArrayForm;

2차원 배열

아래 useFieldArray의 name에서 index에 접근할때 대괄호가아닌 점 표기법으로 접근해야합니다.

const FirstArray = ({ control, firstIndex }: Props) => {
  const {
    fields: secondFields,
    append,
    remove,
  } = useFieldArray({
    control,
    name: `firstArray.${firstIndex}.secondArray`,
  });

  const onAppend = () => {
    append({
      second: '',
      thirdArray: [
        {
          third: '',
        },
      ],
    });
  };

  return (
    <>
      <p>First Array</p>
      <div>
        <span>First : </span>
        <MuiTextField
          control={control}
          name={`firstArray.${firstIndex}.first`}
          rules={{
            required: '필수',
          }}
        />
      </div>
      <ul>
        <p>Second Array</p>
        {secondFields.map((field, secondIndex, arr) => {
          return (
            <li key={field.id}>
              <SecondArray
                arr={arr}
                control={control}
                firstIndex={firstIndex}
                secondIndex={secondIndex}
                remove={remove}
              />
            </li>
          );
        })}
        <button type='button' onClick={onAppend}>
          Append Second Array
        </button>
      </ul>
    </>
  );
};
export default FirstArray;

Mui

import { TextField, TextFieldProps } from '@mui/material';
import { FieldValues, useController } from 'react-hook-form';
import { TControl } from '@/types/type';

type TProps<T extends FieldValues> = TextFieldProps & TControl<T>;

const MuiTextField = <T extends FieldValues>({
  control,
  name,
  rules,
}: TProps<T>) => {
  const {
    field: { value, onChange },
    fieldState: { error },
  } = useController({
    control,
    name,
    rules,
  });

  return (
    <TextField
      variant='outlined'
      value={value}
      onChange={onChange}
      error={!!error}
      helperText={!!error && error.message}
    />
  );
};
export default MuiTextField;

RHK 적용중 실수한 점

form 내부 button 태그로 append시 의도치 않은 validate 실행되는 이슈?

  • form 내부 button태그는 submit가 디폴트값이라 type=button 지정해줘서 해결

useFieldArray의 name에서 점표기법으로 접근해야하는데 대괄호 표기법으로 접근

default value를 제대로 넣지 않아서 append시 제대로 필드 추가가 안되었다.

최종 결과

깃허브 -> https://github.com/Taeyooooon/react-hook-form-study

profile
응애🐣 프론트엔드

0개의 댓글