Typescript useRef array 사용시 ts.(2322) error 해결하기

Maliethy·2021년 11월 13일
0

typscript-error

목록 보기
2/4

1. issue

다음의 에러가 useRef array 사용중 발생했다.

Typescript Type 'RefObject<HTMLInputElement>' is not assignable to type 'Ref<Input>. Type 'RefObject<HTMLInputElement>' is not assignable to type 'RefObject<Input>'.

사용예시는 다음과 같다.
/hooks/useRefInput.ts

import { useRef, KeyboardEventHandler, RefObject, LegacyRef } from 'react';

type ReturnType<T> = [
  RefObject<T>[],
  (e: any) => (index: number) => KeyboardEventHandler<HTMLInputElement>,
];
function useRefInputs<T>(ref_len: number): ReturnType<T> {
  const ref_inputs = [];

  for (let i = 0; i < ref_len; i++) {
    ref_inputs[i] = useRef(null);
  }

  const handleFocusNext =
    (e) =>
    (index): ((e: any) => (index: number) => KeyboardEventHandler<HTMLInputElement>) => {
      if (ref_inputs[index + 1] && index < ref_inputs.length - 1) {
        ref_inputs[index + 1].current?.focus();
        return;
      }
      if (ref_inputs[index + 1] && index == ref_inputs.length - 1) {
        ref_inputs[index].current?.blur();
        return;
      }
    };

  return [ref_inputs, handleFocusNext];
}

export default useRefInputs;

/pages/Login.tsx

import { Form, Input } from 'formik-antd';
import useRefInputs from '@hooks/useRefInputs';

  const [ref_inputs, handleFocusNext] = useRefInputs<HTMLInputElement>(3);
  
  
  const handleSubmitFailed = useCallback(() => {
    if (userID === undefined || userID.trim() === '') {
      ref_inputs[0].current.focus();
    } else {
      ref_inputs[1].current.focus();
    }
  }, [userID]);
  
  
  
  return (
                 ...
  
                      <LoginInput
                      ref={ref_inputs[0]}
                      onPressEnter={() => handleFocusNext(0)}
                      placeholder="아이디"
                      maxLength={15}
                      value={userID}
                      onChange={handleUserID}
                    />
                    
                                        <PasswordInput
                      allowClear
                      ref={ref_inputs[1]}
                      onPressEnter={() => handleFocusNext(1)}
                      placeholder="비밀번호"
                      maxLength={20}
                    />
                                        <Button
                      ref={ref_inputs[2]}
                      type="primary"
                      loading={PostSignInLoading}
                      htmlType="button"
                      onClick={handleSubmitButton}
                    >
                      로그인
                    </Button>
      
              ...
  );

2. solution

DOM이 처음 rendering되면 ref_inputs이란 배열에 아래 스크린샷과 같이 current에 input값이 채워져있다.

formik-antd의 input의 타입은 다음과 같았다

const TypedInput = (Input as unknown) as InputType

>>>

interface InputType
  extends React.ForwardRefExoticComponent<
    FormikFieldProps & $InputProps & React.RefAttributes<$Input>
  > {
  Password: React.ForwardRefExoticComponent<
    FormikFieldProps & $PasswordProps & React.RefAttributes<Password>
  >
  TextArea: React.ForwardRefExoticComponent<
    FormikFieldProps & $TextAreaProps & React.RefAttributes<TextArea>
  >
}

>>>
interface RefAttributes<T> extends Attributes {
        ref?: Ref<T> | undefined;
    }
    

import { Input } from 'antd';와 같이 antd의 Input component를 사용한 경우에 Input 안의 ref property의 타입은 다음과 같았다.

interface ClassAttributes<T> extends Attributes {
        ref?: LegacyRef<T> | undefined;
    }

antd-form와 antd Input의 ref type을 맞춰주기 위해

React.RefAttributes<$Input> LegacyRef로 타입을 지정해주어 해결했다. 아래와 같이 LegacyRef에는 Ref 또한 타입으로 인식할 수 있기 때문에 LegacyRef 타입을 아래 부분에 추가해서 해결했다.

type LegacyRef<T> = string | Ref<T>;

/hooks/useRefInput.ts

import { useRef, KeyboardEventHandler, RefObject, LegacyRef } from 'react';

type ReturnType<T> = [
  RefObject<T>[] | LegacyRef<T>,//LegacyRef<T> 타입 추가
  (e: any) => (index: number) => KeyboardEventHandler<HTMLInputElement>,
];
function useRefInputs<T>(ref_len: number): ReturnType<T> {
  const ref_inputs = [];

  for (let i = 0; i < ref_len; i++) {
    ref_inputs[i] = useRef(null);
  }

  const handleFocusNext =
    (e) =>
    (index): ((e: any) => (index: number) => KeyboardEventHandler<HTMLInputElement>) => {
      if (ref_inputs[index + 1] && index < ref_inputs.length - 1) {
        ref_inputs[index + 1].current?.focus();
        return;
      }
      if (ref_inputs[index + 1] && index == ref_inputs.length - 1) {
        ref_inputs[index].current?.blur();
        return;
      }
    };

  return [ref_inputs, handleFocusNext];
}

export default useRefInputs;

참고
https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/hooks/#useref
https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5

profile
바꿀 수 있는 것에 주목하자

0개의 댓글