useRef(initialValue)
- useRef는 렌더링에 필요하지 않은 값을 참조할 수 있는 React 훅입니다.
참조
선언하기
- 컴포넌트의 최상위 레벨에서 useRef를 호출하여 ref를 선언합니다.
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
매개변수
- initialValue : ref 객체의 current 프로퍼티 초기 설정값입니다. 여기에는 어떤 유형의 값이든 지정할 수 있고, 초기 렌더링 이후부터는 무시됩니다.
반환값
- useRef는 단일 프로퍼티를 가진 객체를 반환합니다.
- current: 처음에는 전달한 initialValue로 설정됩니다. 나중에 다른 값으로 바꿀 수 있습니다.
- ref 객체를 JSX 노드의 ref 속성으로 React에 전달하면 React는 current 프로퍼티를 설정합니다.
- 다음 렌더링에서 useRef는 동일한 객체를 반환합니다.
주의사항
- ref.current 프로퍼티는 state와 달리 변이할 수 있습니다. 그러나 렌더링에 사용되는 객체(state)를 포함하는 경우 해당 객체를 변이해서는 안됩 니다.
- ref.current 프로퍼티를 변경해도 React는 컴포넌트를 다시 렌더링하지 않습니다. ref는 일반 JavaScript 객체이기 때문에 React는 사용자가 언제 변경했는지 알지 못합니다.
- 초기화를 제외하고는 렌더링 중에 ref.current를 쓰거나 읽지마세요. 이렇게 하면 컴포넌트의 동작을 예측할 수 없게 됩니다.
- Strict Mode에서 React는 컴포넌트 함수를 두 번 호출하여 의도하지 않은 불순물을 찾을 수 있도록 돕습니다. 각 ref 객체는 두 번 생성되고 그 중 하나는 버려집니다.
사용법
😀 ref로 값 참조하기
- 컴포넌트의 최상위 레벨에서 useRef를 호출하여 하나 이상의 ref를 선언합니다.
function Stopwatch() {
const intervalRef = useRef(0);
- useRef는 처음에 제공한 초기값으로 설정된 단일 current 프로퍼티가 있는 ref 객체를 반환합니다.
- 다음 렌더링에서 useRef는 동일한 객체를 반환합니다. 정보를 저장하고 읽을 수 있도록 current 속성을 변경할 수 있습니다.
- ref를 변경해도 리렌더링을 촉발하지 않습니다. 즉, ref는 컴포넌트의 시각적 출력에 영향을 미치지 않는 정보를 저장하는 데 적합합니다.
- 예) intervalID를 저장 했다가 나중에 불러와여 하는 경우 ref에 넣을 수 있습니다.
- ref 내부의 값을 업데이트 하는 경우 current 프로퍼티를 수동으로 변경해야합니다.
- 나중에 ref에서 해당 interval ID를 읽어 해당 interval을 취소할 수 있습니다
function handleStartClick() {
const intervalId = setInterval(() => {
}, 1000);
intervalRef.current = intervalId;
}
function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}
- ref를 사용하면 다음을 보장합니다:
- (렌더링할 때마다 재설정되는 일반 변수와 달리) 리렌더링 사이에 정보를 저장할 수 있습니다.
- (리렌더링을 촉발하는 state 변수와 달리) 변경해도 리렌더링을 촉발하지 않습니다.
- (정보가 공유되는 외부 변수와 달리) 각각의 컴포넌트에 로컬로 저장됩니다.
ref - data 사용 예시
export default function Stopwatch() {
const [startTime, setStartTime] = useState(null);
const [now, setNow] = useState(null);
const intervalRef = useRef(null);
function handleStart() {
setStartTime(Date.now());
setNow(Date.now());
clearInterval(intervalRef.current);
intervalRef.current = setInterval(() => {
setNow(Date.now());
}, 10);
}
function handleStop() {
clearInterval(intervalRef.current);
}
let secondsPassed = 0;
if (startTime != null && now != null) {
secondsPassed = (now - startTime) / 1000;
}
return (
<>
<h1>Time passed: {secondsPassed.toFixed(3)}</h1>
<button onClick={handleStart}>
Start
</button>
<button onClick={handleStop}>
Stop
</button>
</>
);
}
주의!
- 렌더링 중에는 ref.current를 쓰거나 읽지 마세요.
- React는 컴포넌트의 본문이 순수 함수처럼 동작하기를 기대합니다
- 입력값들(props, state, context)이 동일하면 완전히 동일한 JSX를 반환해야 합니다.
- 다른 순서나 다른 인수를 사용하여 호출해도 다른 호출의 결과에 영향을 미치지 않아야 합니다.
- 렌더링 중에 ref를 읽거나 쓰면 이러한 기대가 깨집니다.
function MyComponent() {
myRef.current = 123;
return <h1>{myOtherRef.current}</h1>;
}
function MyComponent() {
useEffect(() => {
myRef.current = 123;
});
function handleClick() {
doSomething(myOtherRef.current);
}
}
- 렌더링 중에 써야하는 경우라면 state를 사용하세요.
😀 ref로 DOM 조작하기
- ref를 사용하여 DOM을 조작할 수 있습니다.
- 먼저 초기값이 null인 ref 객체를 선언하세요.
- 그런 다음 ref 객체를 ref 속성으로 조작하려는 DOM 노드의 JSX에 전달하세요.
function MyComponent() {
const inputRef = useRef(null);
return <input ref={inputRef} />;
- React가 DOM 노드를 생성하고 화면에 그린 후, React는 ref 객체의 current프로퍼티를 DOM 노드로 설정합니다. 이제 DOM 노드 input에 접근해 focus()와 같은 메서드를 호출할 수 있습니다.
function handleClick() {
inputRef.current.focus();
}
- 노드가 화면에서 제거되면 React는 current 프로퍼티를 다시 null로 설정합니다.
ref DOM 조작 예시
- 이미지 스크롤하기
- 이 예제에서는 버튼을 클릭하면 이미지가 스크롤됩니다. 목록 DOM 노드에 대한 ref를 사용한 다음 DOM querySelectorAll API를 호출하여 스크롤하려는 이미지를 찾습니다.
export default function CatFriends() {
const listRef = useRef(null);
function scrollToIndex(index) {
const listNode = listRef.current;
const imgNode = listNode.querySelectorAll('li > img')[index];
imgNode.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
});
}
return (
<>
<nav>
<button onClick={() => scrollToIndex(0)}>
Tom
</button>
<button onClick={() => scrollToIndex(1)}>
Maru
</button>
<button onClick={() => scrollToIndex(2)}>
Jellylorum
</button>
</nav>
<div>
<ul ref={listRef}>
<li>
<img
src="https://placekitten.com/g/200/200"
alt="Tom"
/>
</li>
<li>
<img
src="https://placekitten.com/g/300/200"
alt="Maru"
/>
</li>
<li>
<img
src="https://placekitten.com/g/250/200"
alt="Jellylorum"
/>
</li>
</ul>
</div>
</>
);
}
- 컴포넌트에 ref 노출하기
- 때로는 부모 컴포넌트가 컴포넌트 내부의 DOM을 조작할 수 있도록 하고 싶을 때가 있습니다.
- 예를 들어, MyInput 컴포넌트를 작성하는 중인데, 부모 컴포넌트가 (부모가 접근할 수 없는) MyInput의 input에 초점을 맞출 수 있게 하고 싶을 수 있습니다.
- useRef로 input을 붙잡고 forwardRef로 이를 부모 컴포넌트에 노출시킬 수 있습니다.
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
😀 ref로 콘텐츠 재생성 피하기
- React는 초기에 ref 값을 한 번 저장하고, 다음 렌더링부터는 이를 무시합니다.
- new VideoPlayer()의 결과는 초기 렌더링에만 사용되지만, 호출 자체는 이후의 모든 렌더링에서도 여전히 계속 이뤄집니다. 이는 값비싼 객체를 생성하는 경우 낭비일 수 있습니다.
function Video() {
const playerRef = useRef(new VideoPlayer());
- 이 문제를 해결하려면 대신 다음과 같이 ref를 초기화할 수 있습니다
function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
- 일반적으로 렌더링 중에 ref.current를 쓰거나 읽는 것은 허용되지 않습니다. 하지만 이 경우에는 결과가 항상 동일하고 초기화 중에만 조건이 실행되므로 충분히 예측할 수 있으므로 괜찮습니다.
useRef를 초기화할 때 null 검사를 피하는 방법
- 타입 검사기를 사용하면서 항상 null을 검사하고 싶지 않다면 다음과 같은 패턴을 대신 사용해 볼 수 있습니다
function Video() {
const playerRef = useRef(null);
function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
const player = new VideoPlayer();
playerRef.current = player;
return player;
}
- 여기서 playerRef 자체는 nullable합니다. 하지만 타입 검사기에 getPlayer()가 null을 반환하는 경우가 없다는 것을 확신시킬 수 있어야 합니다. 그런 다음 이벤트 핸들러에서 getPlayer()를 사용하십시오.
문제 해결
😀 커스텀 컴포너트에 대한 ref를 얻을 수 없습니다.
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
- 다음과 같은 오류가 발생합니다.

- 기본적으로 컴포넌트는 내부의 DOM 노드에 대한 ref를 외부로 노출하지 않습니다.
- 이 문제를 해결하려면 ref를 가져오고자 하는 컴포넌트를 찾으세요.
- 그런 다음 다음과 같이 forwardRef로 감싸세요.
const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});
export default MyInput;
- 그러면 부모 컴포넌트의 ref를 가져올 수 있습니다.
좋은 글 잘 읽고 갑니다 ㅎㅎ