React에서는 DOM을 직접 조작할 수 없는데, focus나 text selection, 미디어의 재생을 관리할 때, 애니메이션을 직접적으로 실행시킬 때 등 DOM 엘리먼트의 주소값을 활용해야 하는 상황이 발생한다.
이때 사용할 수 있는 Hook이 useRef 이다.
(DOM 노드, 엘리먼트, 그리고 React 컴포넌트 주소값을 참조할 수 있다.)
아래 코드처럼 작성하면 주소값을 활용할 수 있으며, 컴포넌트가 리 렌더링 되어도 주소값은 그대로 유지된다.
const inputRef = useRef(참조자료형)
return (
<div>
<input ref={inputRef} type="text" />
{/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면*/}
{/* inputRef에는 input DOM 엘리먼트의 주소가 담긴다. */}
{/* 향후 다른 컴포넌트에서 input DOM 엘리먼트를 활용할 수 있다. */}
</div>);
useRef를 사용하는 몇 가지 예시로 살펴보자.
컴포넌트에 focus를 위치시킬 필요가 있는 경우
예를 들어, Enter 키를 입력했을 때 다음 input으로 focus를 이동시켜야 한다면, 다음과 같이 코드를 작성하면 된다.
import React, { useRef } from "react";
const Focus = () => {
const firstRef = useRef(null);
const secondRef = useRef(null);
const thirdRef = useRef(null);
const handleInput = (event) => {
console.log(event.key, event);
if (event.key === "Enter") {
if (event.target === firstRef.current) { // 첫 번째 input에서 엔터를 눌렀을 때
secondRef.current.focus(); // 두 번째 input으로 focus
} else if (event.target === secondRef.current) {
thirdRef.current.focus();
} else if (event.target === thirdRef.current) {
firstRef.current.focus();
} else {
return;
}
}
};
return (
<div>
<div>
<label>input 1 </label>
<input ref={firstRef} onKeyUp={handleInput} />
</div>
<div>
<label>input 2 </label>
<input ref={secondRef} onKeyUp={handleInput} />
</div>
<div>
<label>input 3 </label>
<input ref={thirdRef} onKeyUp={handleInput} />
</div>
</div>
);
};
export default Focus;
const firstRef = useRef(null)
: Ref 객체를 만들어준다.
<input ref={firstRef} onKeyUp={handleInput} />
: 주소값을 활용하고자 하는 DOM에 속성으로 ref값을 설정해준다. (이벤트 핸들러 설명 생략)
secondRef.current.focus()
: Ref 객체의 current 값은 해당 DOM을 가리킨다. 해당 DOM에 포커싱을 해주는 focus()를 호출한다.
useRef로 컴포넌트 안의 변수 관리하기
리 렌더링을 하지 않으면서 컴포넌트의 속성 정보를 조회 & 수정할 때 useRef를 활용한 변수는 아래와 같은 곳에 쓰인다.
아래 코드는 배열에 새로운 항목이 추가될 때 사용할 고유한 id를 변수로 관리하는 예시이다.
import React, { useRef } from 'react';
import UserList from './UserList';
function App() {
const users = [
{
id: 1,
username: 'curry',
email: 'curry@example.com'
},
{
id: 2,
username: 'user1',
email: 'user1@example.com'
},
{
id: 3,
username: 'user2',
email: 'user2@example.com'
}
];
const nextId = useRef(4);
const onCreate = () => {
// 배열에 새로운 항목 추가하는 로직
nextId.current += 1;
};
return <UserList users={users} />;
}
export default App;
const nextId = useRef(4)
: 배열의 고유값 변수로 nextId를 설정해주고, useRef() 파라타미터로 다음 id가 될 숫자 4를 넣어준다. 파라미터 값을 넣어주면 해당 값이 변수의 current 값이 된다. 그리고 nextId 변수를 수정하거나 조회하려면 .current로 접근하면 된다.
nextId.current += 1
: .current로 변수에 접근하여 값을 업데이트 해준다.
여기까지 봤을 때 드는 의문점이 있다. useState와 다른게 뭘까?
state
는 component의 props로 전달하는 등의 방법을 통해 context를 외부에서 접근이 가능하지만useRef
를 통해 생성된ref
의 조작은 정의된 컴포넌트의 내부에서만 가능하다.이러한 특징보다 중요한 것은
useRef
는 내용이 변경될 때 그것을 알려주지 않는다는 점 이다.
.current
프로퍼티의 변경은 어디에서도 관찰 가능하지 않고, 따라서 렌더링을 발생시키지 않는다.
이에 반해state
는useState
를 호출하는 순간 비동기적으로state
를 업데이트함에 따라 컴포넌트를 리렌더링한다는 차이가 있다.