useRef는 current에 어떠한 값을 담고있는 상자를 만드는 React Hook이다.
그 이상 그 이하도 아니다. 예를 들어보자.
const exRef = useRef(1);
위 코드에서 exRef는 그저 1의 값을 담고있는 상자에 불과하다.
우린 current를 통해서 useRef에 있는 값을 사용할 수 있게 된다.
const num = exRef.current; // num = 1
이 사실을 알고, Typescript와 함께 살펴보자.
물론, useRef 자체만으로도 훨씬 더 많은 설명이 필요하지만, Typescript에서의 useRef를 살펴보고싶기 때문에 여기까지만!
3가지의 경우가 있다.
- 제네릭 타입이 인자의 타입과 동일한 경우
useRef<T>(initialValue: T): MutableRefObject<T>;
useRef는 MutableRefObject를 반환한다.
Mutable에서 유추할 수 있듯이, current 그 자체를 변경할 수 있다.
- 인자의 타입이 null이 허용될 경우
useRef<T>(initialValue: T|null): RefObject<T>;
useRef는 RefObject를 반환한다.
이 경우, current 그 자체를 변경하는 것이 불가능하다.
- 제네릭 타입이 undefined(제공하지 않은 경우)인 경우
useRef<T = undefined>(): MutableRefObject<T | undefined>;
MutableRefObject를 반환하지만 타입이 다른 것을 확인할 수 있다.
예시를 통해서 더 자세하게 살펴보자.
const localVarRef = useRef<number>(0);
const handleButtonClick = () => {
if (localVarRef.current) {
localVarRef.current += 1;
console.log(localVarRef.current); // 1
}
};
위 예시는 1번의 경우와 같다. 제네릭 타입이 number로 명시되어있고, 인자값도 0으로 number 타입이다.
이 때는 current 그 자체를 변경할 수 있기 때문에, 콘솔에는 0에서 1이 증가한 1이 출력된다.
그렇다면 인자값을 null로 변경시켜보겠다.
const localVarRef = useRef<number>(null);
const handleButtonClick = () => {
if (localVarRef.current) {
localVarRef.current += 1; // 에러 발생!!!
console.log(localVarRef.current);
}
};
위 예시는 2번의 경우와 같다. 제네릭 타입은 number인데, 인자값에 null이 허용되었기 때문이다.
이 때는 current를 직접 변경할 수 없다. 따라서 에러가 발생한다.
이번에는 예시를 변경해보자.
const App = () => {
const inputRef = useRef<HTMLInputElement>(null);
const handleButtonClick = () => {
if (inputRef.current) {
inputRef.current.value = ""; // 수정 가능!
}
};
return (
<div className="App">
<button onClick={handleButtonClick}>+1</button>
<input ref={inputRef} />
<button onClick={handleButtonClick}>Clear</button>
</div>
);
};
위 코드는 2번의 경우라는 것을 쉽게 알 수 있다.
하지만 이번에는 current를 직접 건드리는게 아닌 current.value를 수정하고 있다.
이 것은 가능하다. 왜 가능할까?
current만 읽기 전용(read only)이기 때문에, current의 하위 프로퍼티인 value는 수정이 가능한 것이다.
이는 읽기 전용이 얕기(shallow) 때문이다.
이번에는 인자값을 지워보겠다.
const inputRef = useRef<HTMLInputElement>();
위 코드는 3번의 경우이다.
이 때는 MutableRefObject를 반환한다.
결과는...에러가 발생한다!
<input ref={inputRef} /> // 에러 발생!!!
이유가 뭘까?
우리가 흔히 사용하는 컴포넌트를 참조하기 위한 ref에는 RefObject만 넣을 수 있기 때문이다.
MutableRefObject와 RefObject는 엄연히 다르기 때문에 에러가 발생하는 것이다.
만약, 로컬 변수 용도로 useRef를 사용하는 것이라면 MutableRefObject를 사용해야하는 것이므로
다음과 같이 사용하자.
const localRef = useRef<number>(0);
만약, DOM을 직접 조작하기 위해 useRef를 사용할 경우, RefObject를 사용해야하므로
다음과 같이 사용하자.
const inputRef = useRef<HTMLInputElement>(null);
글을 정말 잘 쓰셔서 이해가 쏙쏙 되었어요 덕분에 useRef에 대해 타입스크립트까지 제대로 공부하고 가네요 감사합니다~