frameworks/react
useRef 이해하기, useRef의 두 가지 기능: 1. Dom제어 2. re-render시에도 격납한 값이 불변
iliosncelini
2021. 3. 25. 18:46
useRef 첫 번째 기능: Component의 Dom 취득하기
시험해볼 소스코드는 다음과 같습니다.
import React, { useState, useRef } from 'react';
function InputSample() {
const [inputs, setInputs] = useState({name: ''});
const nameInput = useRef();
const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출
const onChange = e => {
const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
setInputs({
...inputs, // 기존의 input 객체를 복사한 뒤
[name]: value // name 키를 가진 값을 value 로 설정
});
};
const onReset = () => {
setInputs({name: ''});
nameInput.current.focus();
};
return (
<div>
<input
name="name"
placeholder="이름"
onChange={onChange}
value={name}
ref={nameInput}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
}
export default InputSample;
useRef에 정말 컴포넌트의 Dom이 들어와있는지 아닌지를 확인하기 위한 목적의 어플입니다. 상단부에서 useRef를 선언하고, 컴포넌트에 useRef에 의해 생성된 인스턴스를 붙여줍니다. ref={nameInput}와 같이 컴포넌트에 ref={인스턴스}의 방법으로 useRef를 컴포넌트에 부착시킬 수 있습니다.
어플의 요건은 단순합니다. 하나의 input이 있고, reset버튼을 누르면 아래와 같이 작동합니다.
inputstate가 초기화되고, 컴포넌트가 re-renderinput에 포커싱


위의 그림처럼 초기화버튼을 클릭하자 input에 focusing이 된 것을 볼 수 있습니다.(포커싱 되면 까만 테두리로 표현돼요)

ref.current에 정말 dom이 들어와있는지, 개발자 도구로 확인해봅시다. input의 dom이 잘 들어와 있네요. 따라서 ref.current를 제어해서 dom 내부 함수를 사용할 수 도 있고, Dom내부의 값을 빼올 수 도 있습니다.
useRef 두 번째 기능: re-render시에도 격납한 값이 불변
import React, { useState, useRef } from "react"
function ManualCounter() {
const [count, setCount] = useState(0)
const intervalId = useRef(null)
console.log(`랜더링... count: ${count}`)
const startCounter = () => {
intervalId.current = setInterval(() => setCount((count) => count + 1), 1000)
console.log(`시작... intervalId: ${intervalId.current}`)
}
const stopCounter = () => {
clearInterval(intervalId.current)
console.log(`정지... intervalId: ${intervalId.current}`)
}
return (
<>
<p>자동 카운트: {count}</p>
<button onClick={startCounter}>시작</button>
<button onClick={stopCounter}>정지</button>
</>
)
}
위 어플의 전제적인 흐름은 다음과 같습니다.
- 시작 버튼 클릭
- 1초에 한번씩
countstate를 업데이트 - state가 업데이트 될 때마다
ManualCounter컴포넌트 re-rendering
(위의 과정 반복..)
위의 어플을 기동시키면 다음과 같은 console을 얻을 수 있습니다.
시작... intervalId: 4
랜더링... count: 1
랜더링... count: 2
랜더링... count: 3
랜더링... count: 4
정지... intervalId: 4
시작... intervalId: 5
랜더링... count: 5
랜더링... count: 6
랜더링... count: 7
랜더링... count: 8
정지... intervalId: 5
콘솔에서 시작할 때의 intervalId와 정지할 때의 intervalId가 동일한 것을 알 수 있습니다. 시작시 intervalId.current에 격납해 두었던 intervalId가 컴포넌트가 여러번 re-render된 뒤에도 값이 초기화되지 않고 intervalId.current에 저장되어 있었음을 의미하죠.