본문 바로가기

FrontEnd/react

React.forwardRef with typescript generics

react-hook-form 과 mui의 컴포넌트로 form 입력 컴포넌트들을 만들던 도중 상위 컴포넌트로 부터 하위 컴포넌트로 ref 를 전달해야하는 상황이 있었다. 일반적으로 props 를 하위 컴포넌트로 전달하는 방식처럼 전달했더니 아래와 같은 warning이 발생했다

공식문서 에 따르면 key와 마찬가지로 ref는 React에서 다르게 처리하기 때문에 props 전달 방식으로는 하위 컴포넌트로 전달할 수 없다. 

 

해당 warning을 해결하기 위해서는 warning message 에 나온 것처럼 React.forwardRef 를 사용하여 ref를 전달받을 컴포넌트를 감싸주면 된다. 컴포넌트를 React.forwardRef 로 감싸면 두 번째 매개변수로 ref 를 전달 받기 때문에 이를 통해 외부에서 ref 를 넘길 수 있다

 

그런데 문제가 한가지 더 있었다. 개발중인 컴포넌트가 generic을 사용하는 컴포넌트였으나 forwardRef에는 컴포넌트의 generic을 주입할 방법이 없다는 문제가 있었다. 

 

생각보다 해결법이 간단한데, typescript의 type assertion으로 컴포넌트의 타입을 "단언" 해주면 된다. 

 

아래는 실제로 해당 문제를 해결한 코드 예시 이다.

 

import { FormControl, InputLabel, Select, SelectProps } from '@mui/material'
import { forwardRef, PropsWithChildren, ReactElement } from 'react'

function LabelSelect<T>({ children, label, size, value, ...selectProps }: PropsWithChildren<SelectProps<T>>) {
  return (
    <FormControl size={size} variant="outlined">
      <InputLabel id={selectProps.labelId}>{label}</InputLabel>
      <Select size={size} value={value || ''} {...selectProps}>
        {children}
      </Select>
    </FormControl>
  )
}

const ForwardRefLabelSelect = forwardRef((props, ref) => <LabelSelect {...props} {...ref} />) as <T>({
  ...props
}: PropsWithChildren<SelectProps<T>>) => ReactElement

export default ForwardRefLabelSelect

위 코드의 as <T>({... }) => .... 처럼 ForwardRef로 감싸여진 컴포넌트를 type assertion으로 타입을 '단언' 해주면 제네릭 컴포넌트를 사용할 수 있다.