이 전에 컴포넌트 분리가 잘 안되었던 것 같아서
이번에는 재사용 가능한 컴포넌트들을 최대한 분리해보려고 한다.
그래서 이번에 원티드 프리온보딩에서 라벨, 인풋, 에러메시지를 하나로 묶어서 하나의 컴포넌트로 만들고 여기서 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를 명시할 수 있게 해주었다.