다음의 에러가 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>
...
);
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