useRef
는 렌더링에 필요하지 않은 값을 참조할 수 있는 React 훅이다. - 📎 공식문서
우리는 값을 저장하는 개념을 3가지로 나누어서 생각해볼 수 있다.
변수
-UI상 표현되지 않는 값이다.
-state 변화를 통해 UI를 렌더링 하는 순간 변수가 초기화된다.
state
-UI상 표현되는 값이다.
-화면을 새로고침하지 않는 한, 값이 유지된다.
-화면에 보여지는 값이기 때문에 state가 변화할 때마다 매번 컴포넌트가 마운트되고, 리렌더링이 일어난다. 이것은 성능상 문제로 이어질 수 있다.
useRef
-UI상 표현되지 않는 값이다.
-라이프 사이클 주기동안 값이 유지된다.
Changing a ref does not trigger a re-render. This means refs are perfect for storing information that doesn’t affect the visual output of your component. - 공식문서 중
공식문서는 useRef를 사용하면 ref를 변경해도 리렌더링을 촉발하지 않아서 컴포넌트의 시각적 출력에 영향을 미치지 않는 정보를 저장하는 데 적합하다고 설명한다.
따라서 useRef의 특징을 다음과 같이 정리할 수 있다.
사용자가 똑같은 검색어를 입력하면 불필요한 api 호출이 발생한다. 이럴 때 useRef를 이용해서 지난 검색어를 저장한 후, 버튼을 눌렀을 때 useRef의 current값이 현재 value와 동일하다면 api 호출하지 않도록 만들 수 있다.
import { ChangeEventHandler, useRef, useState } from 'react';
export default function Home() {
const [keyword, setKeyword] = useState('');
const prevKeywordRef = useRef('');
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) =>
setKeyword(e.target.value);
const fetchSearch = () => console.log('api 호출 시작! ');
const handleSearch = () => {
if (prevKeywordRef.current !== keyword) {
fetchSearch();
prevKeywordRef.current = keyword;
}
};
return (
<form>
<input
type="text"
value={keyword}
onChange={handleChange}
placeholder="검색어를 입력하세요"
/>
<button
type="button"
onClick={handleSearch}
>
검색
</button>
</form>
);
}
<input type="file" />
은 다음과 같이 나타난다.
남들은 이렇게 UI를 개선하던데, 어떻게 하는 것일까? 하던 과거의 무지했던 나..
답은 useRef에 있었다!
'use client';
import React, { ChangeEventHandler, useRef, useState } from 'react';
export default function Page() {
const imageRef = useRef<HTMLInputElement>(null);
const [profileImg, setProfileImg] = useState<File>();
const onClickButton = () => imageRef.current?.click();
const handleChangeImage: ChangeEventHandler<HTMLInputElement> = (e) => {
const files = e.target?.files;
if (files && files[0]) {
setProfileImg(files[0]);
}
};
return (
<div>
<div>
<label
htmlFor="image"
className="font-bold"
hidden
>
프로필 이미지
</label>
<input
id="image"
name="image"
type="file"
accept="image/*"
hidden
ref={imageRef}
onChange={handleChangeImage}
/>
<button
type="button"
onClick={onClickButton}
>
{/* 원하는 버튼 이미지 (svg파일 등) */}
</button>
</div>
</div>
);
}
<input type="file" />
을 hidden
으로 만들고, ref을 만들어 DOM조작이 가능하게 한다. button
의 onClick 이벤트 헨들러에 () => imageRef.current?.click();
을 추가하여 ref의 current을 이용하여 input DOM요소를 대신 click하도록 만든다. 컨텐츠가 많은 사이트를 보면 어디에서나 볼 수 있는 버튼이 있다.
이렇게 생긴 디자인의 버튼을 한 번쯤 본 적이 있을 것이다.
useRef을 이용하면 뷰포트 상단으로 이동하는 기능을 쉽게 만들 수 있었다 !
import { useRef } from 'react';
export default function ScroppToTop() {
const topRef = useRef<HTMLDivElement | null>(null);
const handleToTop = () => {
if (topRef.current) {
topRef.current.scrollIntoView({ behavior: 'smooth' });
}
};
return (
<div>
<div ref={topRef} />
<button onClick={handleToTop}> 최상단 이동</button>
</div>
);
}
화면의 최상단 부분에 해당하는 컨텐츠에 ref를 달고, button의 onClick 이벤트에 scrollIntoView
를 이용하여 화면 상단으로 이동시킬 수 있다.
공식문서에서 scrollIntoView
를 이용하여 각 이미지에 해당하는 버튼을 누르면 해당 이미지로 포커스되어 이동하는 예제도 있으니 참고하면 좋겠다. - 📎공식문서