본문으로 바로가기

tip) useRef가 중요한 것이 아니라 타이핑이 되어 있는 정의 부분으로 이동하여 살펴보는 것이 중요함을 강조하기 위함이고, useRef는 구체적인 실천의 예시일 뿐입니다.

 

일반 js를 사용할 때는 대두되지 않은 문제였지만, TS를 사용할 때 useRef에는 3가지 종류가 있습니다.

 

 

@types/react 에서 정의된 useRef의 d.ts를 살펴보면(굳이 찾을 필요 없이 useRef에 ctrl + click하면 됩니다.)

 

useRef에 3가지 종류가 있음을 확인할 수 있습니다. 정확히 말하자면 하나의 useRef인데 인자나 제네릭 타입에 따라 다른 것을 사용하도록 overload된 것입니다. (커서를 올려보면 +2 overloads가 있다고 확인할 수 있습니다)

 

useRef에 커서를 올려보면 다음과 같은 설명을 확인할 수 있습니다. 현재 사용중인 useRef는 MutableRefObject를 반환합니다. 가장 일반적인 형태라고 볼 수 있겠습니다.

 

 

1. 

매개 변수가 제네릭과 일치하는 useRef입니다.

// TODO (TypeScript 3.0): <T extends unknown>
function useRef<T>(initialValue: T): MutableRefObject<T>;
    
// convenience overload for refs given as a ref prop as they typically start with a null value
    
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
* value around similar to how you’d use instance fields in classes.
*
* Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
* of the generic argument.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
     

그래서 구체적으로 MutableRefObject가 무엇인지 확인해봤더니 그냥 단순하게 <T> 제네릭으로 넘겨준 타입의 current 프로퍼티를 가진 객체였군요.

interface MutableRefObject<T> {
    current: T;
}

 

 

2. 

초기값에 null이 될 수 있는 유니언 타입을 지정하면 RefObject를 반환합니다. 

// TODO (TypeScript 3.0): <T extends unknown>
function useRef<T>(initialValue: T|null): RefObject<T>;
    
// convenience overload for potentially undefined initialValue / call with 0 arguments
// has a default to stop it from defaulting to {} instead
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
* value around similar to how you’d use instance fields in classes.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/

 

RefObject가 무엇인이 찾아보니 MutableRefObject와 다르게 readonly 속성이기 때문에 값을 변화시킬 수 없습니다.

interface RefObject<T> {
    readonly current: T | null;
}

 

2번과 같은 이러한 종류의 useRef면 current에 내용을 주입하는 용도로 useRef를 사용할 수 없습니다. current가 readonly 이기 때문입니다.

 

useRef는 보통 특정 DOM을 선택, 조작해야할 때 사용한다고 알려져 있지만, 꼭 그렇지만은 않습니다. useRef를 생성한 후 ref를 컴포넌트에 붙이지 않고 사용할 수도 있습니다. 값이 바뀌어도 화면이 리렌더링되지 않게 하기 위해서 사용하는 경우도 있습니다. (그리고 아시다시피 js에서는 함수도 값처럼 취급하므로 변수로 지정할 수 있습니다. )

 

이를 이용해서 다음과 같이 정의해보려고 했습니다. js에서는 아무 문제 없이 돌아가는 코드가.. TS에서는 작동하지 않습니다. 이러한 이유는 useRef의 3가지 종류 중 적절한 것을 사용하지 못했기 때문입니다.

 

const timeout = useRef(null); // RefObject 반환해서 current가 readonly가 됨

timeout.current = setTimeout(() => {
  startTime.current = new Date().getTime(); // Error!
}, Math.floor(Math.random() * 1000) + 2000);

// const timeout = useRef<number | null>(null); // MutableRefObject을 사용하기 위함
// 이것으로 교체해줘야 오류가 안 남

 

 

3.

제네릭에 undefined를 넣는 경우입니다. MutableRefObject를 반환합니다.

제네릭이 undefined인 경우 사용되며, undefined이므로 매개 변수가 아예 없네요.

// TODO (TypeScript 3.0): <T extends unknown>
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
/**
* The signature is identical to `useEffect`, but it fires synchronously after all DOM mutations.
* Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside
* `useLayoutEffect` will be flushed synchronously, before the browser has a chance to paint.
*
* Prefer the standard `useEffect` when possible to avoid blocking visual updates.
*
* If you’re migrating code from a class component, `useLayoutEffect` fires in the same phase as
* `componentDidMount` and `componentDidUpdate`.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#uselayouteffect
*/

 

 

useRef를 쓰면서 무언가 에러가 나면 이 셋 중 어떤 useRef를 사용중인지를 체크해보면 됩니다.

 

 

 

* 간략하게 요약을하자면...

 

const menuRef = useRef<number>(0); // ref에 특정 값을 담는 용으로 사용할 때
const menuRef = useRef<HTMLInputElement>(null); // DOM을 다룰 때 반드시 초깃값은 null로 설정한다.

 

'React, Next, Redux > ⚛ React.TS' 카테고리의 다른 글

styled-components + typescript  (0) 2020.08.26
React에서 Typescript 사용하기  (0) 2020.03.18

darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체