Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-hook-form 사용 시 register를 프롭스로 넘기는 경우 ref 에러 #5

Open
DearYuto opened this issue Mar 16, 2024 · 0 comments
Assignees

Comments

@DearYuto
Copy link
Owner

DearYuto commented Mar 16, 2024

이슈

에러 메시지

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()

원인 코드

   <LabelInput
    // ... 생략
    {...register(
      item.name,
      item.name === 'rePassword'
        ? {
            required: item.errorMessage,
            validate: validatePasswordConfirm,
          }
        : {
            required: item.errorMessage,
            pattern: item.pattern,
          }
    )}
  />

LabelInput 컴포넌트 프롭스로 register를 호출한 리턴 값을 넘겨주는 부분에서 발생한 에러.
forwardRef로 해결할지, 다른 방법(useEffect 사용?)을 고민하던 중
Controller 컴포넌트로 해결하기로 결정.

동일한 에러 발생

아무래도 react-hook-form에서 ref가 포함된 객체가 있어서 생기는 문제인 것으로 예상된다.
프롭스로 이 ref가 포함된 객체를 넘겨주고 있기 때문에 forwardRef를 추가하는 방법으로 해결하고자 한다.

const LabelInput = forwardRef(function LabelInput(
  { label, inputType, labelFor, ariaInvalid, placeholder, required = false, ...rest }: Props,
  ref: React.ForwardedRef<HTMLInputElement>
) {
  return (
    <>
      <label htmlFor={labelFor}>{label}</label>
      <input
        ref={ref}
        placeholder={placeholder}
        type={inputType}
        required={required}
        aria-required={required ? 'true' : 'false'}
        aria-invalid={ariaInvalid}
        id={labelFor}
        {...rest} // 1. 여기 rest로 ref가 들어오기 때문에 문제 발생
      />
    </>
  );
});

export default LabelInput;

register로 롤백 안하고 컨트롤러 컴포넌트는 그대로 이용하기로 했다.

<Controller
      key={item.id}
      control={control}
      name={item.name}
      rules={
        item.name === 'rePassword'
          ? {
              required: item.errorMessage,
              validate: validatePasswordConfirm,
            }
          : {
              required: item.errorMessage,
              pattern: item.pattern,
            }
      }
      render={({ field }) => {
        return (
          <>
            <LabelInput
              required
              label={item.label}
              labelFor={item.name}
              ariaInvalid={isSubmitted ? (errors.email ? 'true' : 'false') : undefined}
              placeholder={item.placeholder}
              inputType={item.type}
              {...field}  // 여기서 field를 그냥 넘겨주고 있어서 ref가 포함됨
            />

// ... 생략

다른 이슈 발생 - 언컨트롤드 인풋과 value

일단 ref 이슈는 해결했는데, 문제는 ref로 조작되는 부분에 뭔가 문제가 있는건지 input에 체인지 이벤트가 발생하면 다음과 같은 에러가 발생한다.

Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components

해결 방안

아무래도 언컨트롤드 인풋으로 value를 던져서 생기는 문제인 것 같다.
(예전에 비슷한 에러를 만났던 기억이 어렴풋이 나서 구조분해 할당으로 value를 제외한 값을 넣어주기로 했다.)

 render={({ field: { onChange, onBlur, ref } }) => {
    return (
      <>
        <LabelInput
          required
          label={item.label}
          labelFor={item.name}
          ariaInvalid={isSubmitted ? (errors.email ? 'true' : 'false') : undefined}
          placeholder={item.placeholder}
          inputType={item.type}
          {...{ onChange, onBlur, ref }}
@DearYuto DearYuto self-assigned this Mar 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant