[React] react-hook-form React.forwardRef 에러 해결

HYEJIN·2023년 1월 26일
0

프로젝트 기록

목록 보기
6/6

시도

이 전에 컴포넌트 분리가 잘 안되었던 것 같아서
이번에는 재사용 가능한 컴포넌트들을 최대한 분리해보려고 한다.

그래서 이번에 원티드 프리온보딩에서 라벨, 인풋, 에러메시지를 하나로 묶어서 하나의 컴포넌트로 만들고 여기서 input 유효성 검사를 편리하게 하기 위해서 react-hook-form을 사용하였다.


문제

근데 디버깅에서 오류가 나진 않았지만 런타임에서 에러를 만나게 되었다.
React.forwardRef에 대한 가이드가 있었다.



const LoginForm = () => {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
    reset,
  } = useForm<FormValue>();

  const onSubmit: SubmitHandler<FormValue> = async (data) => {
    //await api 호출
    console.log(data);
    reset();
  };

  const emailRegister = register('id', {
    required: { value: true, message: '이메일을 입력해주세요.' },
    pattern: { value: Regex.email, message: '이메일 형식을 입력해주세요.' },
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)} css={formStyle}>
      <AuthInput
        type="email"
        placeholder="이메일을 입력해주세요"
        errorMessage={errors?.id?.message}
        {...emailRegister}
      />
      <Button
        width="80px"
        height="30px"
        type="submit"
        css={{ alignSelf: 'center' }}
      >
        로그인
      </Button>
    </form>
  );
};

이 전에는 AuthInput이라는 것이 따로 컴포넌트로 분리되어있던 게 아니라,
해당 로그인 폼 안에서 input 에대한 것들이 모두 들어있었다.
그래서 이 전에는 ref를 내부 컴포넌트로 넘기지 않음으로 이러한 에러를 만나지 않았다.

찾아보니, ref prop은 HTML 엘리먼트 접근할 때 특수한 용도로 사용되기 때문에 일반적인 prop으로 사용을 할 수 없다.
근데 react-hook-form에서 제공하는 register에는 ref가 포함이 되어있어서 이때 발생되는 문제였다.


React.forwardRef API를 사용하여 내부 컴포넌트에 refs를 명시적으로 전달할 수 있다.

 React.forwardRef((props, ref) => {
    return <input {...props} forwardedRef={ref} />;
  }

React.forwardRef는 props와 ref 파라미터를 받아 React 노드를 반환하는 렌더링 함수를 받습니다.

//input.tsx

interface inputProps {
  type: string;
  name: string;
  placeholder: string;
  errorMessage?: string;
}

export const AuthInput = React.forwardRef<HTMLInputElement, inputProps>(
  ({ type, name, placeholder, errorMessage, ...etc }, ref) => {
    return (
      <div css={inputWrapStyle}>
        <label htmlFor={name} css={labelStyle}>
          {name.toUpperCase()}
        </label>
        <input
          css={inputStyle}
          id={name}
          type={type}
          name={name}
          placeholder={placeholder}
          ref={ref}
          {...etc}
        />
        {errorMessage && <ErrorMessage content={errorMessage}></ErrorMessage>}
      </div>
    );
  },
);
const ErrorMessage = ({ content }: { content: string | undefined }) => {
  return <p css={messageStyle}>{content}</p>;
};

export const AuthInput = React.forwardRef<HTMLInputElement, inputProps>(
  ({ type, name, placeholder, errorMessage, ...etc }, ref) => {
    return (
 	  <div>
        <input
          css={inputStyle}
          id={name}
          type={type}
          name={name}
          placeholder={placeholder}
          ref={ref}
          {...etc}
        />
      </div>
    );
  },
);

{type,name,placeholder,errorMessage,...etc}ref
React.forwardRef 콜백함수에 인자로 들어가있다.


정리

리액트에서는 DOM을 선택해 직접 접근하기 위해 ref를 사용하게 된다.
재사용하기 위해서 커스텀하게 input 컴포넌트를 만들었고 ref를 props로 넘겨줄 때 문제가 생겼던 부분이었다.
React.forwardRef api를 활용해서 내부 컴포넌트에 ref를 명시할 수 있게 해주었다.

0개의 댓글