useRef는 저장공간, DOM 요소 접근을 위해 쓰이는 Hook이다.
일반적으로 React에서 DOM Element에 접근할 때 사용하는데 이때 DOM Element의 ref 속성을 이용한다. js의 document.querySelector 같은 애들의 역할이라고 보면 된다.
그럼 저장공간은 무슨 용도일까?
간단히 말하면 일단 useRef는 객체를 반환하는데 이 객체를 그냥 변수처럼 쓴다고 생각하면 된다.
우리가 컴포넌트에서 보통 쓰는 '변수'라고 한다면 state 변수를 떠올릴텐데 그것처럼 컴포넌트 내에서 쓰는 변수라고 생각하면 된다. 하지만 state 변수와는 다른 ref 객체만의 특징이 있기 때문에 state와는 다른 별도의 저장공간이다.
useRef의 이러한 두가지 용도에 대해 알아보며 useRef를 한번 정리해보자.
useRef에 의해 반환된 ref 객체는 두가지 특징이 있다.
우리는 컴포넌트 내에서 일반 변수도 사용하지만 변수값이 변함에 따라 컴포넌트를 리렌더링 시키기 위한 용도로는 state 변수를 사용한다.
컴포넌트가 리렌더링 된다는 것은 컴포넌트 내부의 일반 변수들은 모두 초기화 되고 함수들 또한 다시 선언되어 다시 실행되게 된다는 것을 의미한다.
하지만 리렌더링 될때마다 초기화가 되어버리면 곤란한 변수도 있을 수도 있다. 이럴때 ref 변수를 사용한다면 유용하다.
ref 객체는 컴포넌트가 리렌더링 되어도 초기화되지 않기 때문에 컴포넌트 생애 주기 내에서 값이 유지될 수 있다.
또한 ref 객체는 값이 변해도 컴포넌트가 리렌더링 되지 않기때문에 무분별하게 컴포넌트가 리렌더링 되는것 또한 막을 수 있다.
function App() {
const [render, setRender] = useState(true);
const refValue = useRef(0);
let val = 0;
console.log(`** 렌더링 후 : ref = ${refValue.current} val = ${val} **`);
const increaseRef = () => {
refValue.current = refValue.current + 1;
console.log(`ref = ${refValue.current} val = ${val}`);
}
const increaseVal = () => {
val = val + 1;
console.log(`ref = ${refValue.current} val = ${val}`);
}
return (
<div>
<button onClick={increaseRef}>ref 증가</button>
<button onClick={increaseVal}>val 증가</button>
<button onClick={() => setRender(!render)}>리렌더링</button>
</div>
)
}
ref 증가 버튼을 누르면 ref 변수인 refValue가, val 증가 버튼을 누르면 일반 변수인 val이 1씩 증가한다. 그리고 리렌더링 버튼을 누르면 컴포넌트가 리렌더링되어 val은 다시 0으로 초기화가 되고 refValue는 마지막 값 그대로 보존되어있는 것을 콘솔창에서 볼 수 있는 예시다.
useRef는 current라는 프로퍼티가 있는 객체를 반환하는데, 이 current 프로퍼티의 값은 useRef의 인자로 넣어준 값이 된다.
function App() {
const refValue = useRef(0);
return (
<div>
<button onClick={() => console.log(refValue)}>버튼</button>
</div>
)
}
위에서 ref 변수를 살펴봤을때처럼 useRef를 그저 변수로만 지정해두었을 때의 예시이다.
useRef의 인자로 0을 줘서 refValue라는 ref 변수를 만들고 버튼을 눌러 그걸 콘솔로 찍어보면 다음과 같이 나온다.
refValue는 current라는 프로퍼티가 하나 있는 객체이고 current의 값은 useRef의 인자로 준 0이다.
function App() {
const refValue = useRef(0);
return (
<div>
<button ref={refValue} onClick={() => console.log(refValue)}>버튼</button>
</div>
)
}
하지만 위와 같이 button 요소에 ref 속성에 refValue를 지정한채로 refValue를 콘솔로 찍어보면 이런 결과가 나온다.
current 프로퍼티의 값이 button이다. 그리고 얘를 펼쳐볼수 있는걸 보니 객체인 거 같고 실제로 열어보면 뭔지모를 수많은 프로퍼티들이 나오는 것을 볼 수 있다.
이를 통해 ref 객체는 current라는 프로퍼티를 갖는 객체이고, current의 값은 단순 변수인 경우에는 useRef의 인자로 넣어 초기값으로 정해준 변수의 값, ref 속성으로 지정된 경우에는 해당 DOM 요소 자체라는 것을 알 수 있다.
그리고 ref 객체의 current를 통해 DOM 요소에 접근하는 대표적인 예시가 input 창에 커서를 올려놓는 기능을 구현하는 것이다.
const App = () => {
const inputRef = useRef(null)
const onButtonClick = () => {
inputRef.current.focus()
}
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>
input으로 포커스
</button>
</div>
)
}
input창이 하나 있고 옆에 'input으로 포커스' 라는 버튼이 있다. 이 버튼을 누르면 onButtonClick 함수가 실행되는데 이 함수는 inputRef.current (= input 요소) 에 focus 메서드를 실행시키고 있다.
따라서 버튼을 누르면 input 요소에 커서가 올려지게 된다.
useEffect의 Deps에 빈 배열을 지정하고 이 포커싱 로직을 사용하면 페이지에 접속했을때 특정 input에 자동으로 커서가 놓이게 하는 오토 포커싱 기능을 구현할 수도 있다.
아무튼 이처럼 DOM 요소에 접근하기 위한 용도로 쓰인다는거!
참고자료