React-Hook-Form : Generic 타입 사용

박요셉·2025년 6월 10일

Simple note

목록 보기
14/18
import { Controller, Control, FieldError } from 'react-hook-form';
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from '@/shared/ui/select';
import { ErrorMessage } from '@/shared/ui/errorMessage';
import { cn } from '@/shared/lib/utils';
import { MeFormData } from '../model/type';

const TIER_OPTIONS = [
  { value: 'bronze', label: '브론즈' },
  { value: 'silver', label: '실버' },
  { value: 'gold', label: '골드' },
  { value: 'platinum', label: '플레티넘' },
  { value: 'diamond', label: '다이아몬드' },
  { value: 'ascendant', label: '초월자' },
  { value: 'immortal', label: '불멸' },
  { value: 'radiant', label: '레디언트' },
];

export type TierSelectProps = {
  control: Control<MeFormData>;
  name: 'tier';
  disabled?: boolean;
  error?: FieldError;
  placeholder?: string;
  className?: string;
};


export function TierSelect({
  control,
  name,
  disabled,
  error,
  placeholder = '티어 선택',
  className,
}: TierSelectProps) {
  return (
//...생략
  );
}

위는 내 기준코드이고 이 컴포넌트를 다른 폼에서 사용하려하니 타입이 고정되면 안되겠다 싶어 타입을 제너릭으로 변경하고자하였음 AI와 함께(AI 가)작업함 ㅋㅋ;; 그래서 그를 참고해서 학습한 점을 정리하고자 함

extends FieldValues

일단 FieldValues리액트 훅폼에서 폼의 모든 필드 타입을 의미

type MyForm = {name : string, age : number}

여기서 MyFormFieldValues의 한 종류임

즉, T extends FieldValues를 통해 "T는 react-hook-form에서 사용할 수 있는 폼타입이어야 한다"는 의미를 나타내주어야 control 같은 reactHookForm의 객체?(폼 전체를 관리하는 객체)의 타입이 안정적으로 정해짐

Path

type MyForm = { name: string; age: number; tier: string }
type NameField = Path<MyForm> // "name" | "age" | "tier"
  • Path<T>는 T 타입 객체의 키(key) 중 하나를 의미
  • 타입의 안정성을 위해 enum을 사용하는 느낌스..?

요약

  • FieldValues: react-hook-form에서 사용할 수 있는 폼 타입의 집합
  • Path<T>: 폼 타입 T의 실제 필드명(키)만 허용

아래는 수정한 코드(with AI)

import {
  Controller,
  Control,
  FieldError,
  FieldValues,
  Path,
} from 'react-hook-form';
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from '@/shared/ui/select';
import { ErrorMessage } from '@/shared/ui/errorMessage';
import { cn } from '@/shared/lib/utils';

const TIER_OPTIONS = [
  { value: 'bronze', label: '브론즈' },
  { value: 'silver', label: '실버' },
  { value: 'gold', label: '골드' },
  { value: 'platinum', label: '플레티넘' },
  { value: 'diamond', label: '다이아몬드' },
  { value: 'ascendant', label: '초월자' },
  { value: 'immortal', label: '불멸' },
  { value: 'radiant', label: '레디언트' },
];

export type TierSelectProps<T extends FieldValues> = {
  control: Control<T>;
  name: Path<T>;
  disabled?: boolean;
  error?: FieldError;
  placeholder?: string;
  isMessage?: boolean;
  triggerClassName?: string;
  className?: string;
};

export function TierSelect<T extends FieldValues>({
  control,
  name,
  disabled,
  error,
  placeholder = '티어 선택',
  isMessage = true,
  triggerClassName,
  className,
}: TierSelectProps<T>) {
  return (
 // ...생략
  );
}

<TierSelect<UserListSectionFormData>
    control={control}
    name='tier'
    placeholder='티어 선택'
    triggerClassName='data-[placeholder]:text-white'
    isMessage={false}
/>
  
<TierSelect<MeFormData>
  control={control}
  name='tier'
  disabled={!isEdit}
  error={errors.tier}
/>
// 실제 사용 
profile
개발자 지망생

0개의 댓글