React: useRef 바로알기
Ref
useRef는 ref를 생성한다. ref는 주로 두 가지 일을 한다.
- 렌더링과 관계없는 value를 저장한다.
- DOM을 조작한다.
state vs. ref
state와 ref의 공통점은 둘 다 렌더링 시 값이 유지한다. 그러나 state는 렌더링과 관계있고 ref는 렌더링과 관계가 없다. ref의 변화는 리렌더링을 일으키지 않는다.
또 한 가지 차이점은 ref는 mutable하고 state는 immutable하다. 따라서 state는 set함수를 통해 값을 변경할 수 있고 ref는 current 속성에 바로 접근하여 값을 변경할 수 있다.
state는 값이 바뀌면 reference identity가 바뀌지만 ref는 바뀌지 않는다.
When a piece of information is used for rendering, keep it in state. When a piece of information is only needed by event handlers and changing it doesn’t require a re-render, using a ref may be more efficient. - React docs
reference identity
console.log({} === {}) // false
console.log(Object.is({}, {})) // false
console.log(
Object.is(
() => {},
() => {}
)
) // false
위 코드에서 로그가 false로 나오는 이유는 두 객체의 reference identity가 다르기 때문이다. 객체 리터럴은 항상 새로운 객체를 생성한다. 자바스크립에서 객체 비교는 객체의 구조를 비교하는 것이 아니라 reference identity를 비교 한다.
const obj1 = {}
const obj2 = obj1
console.log(obj1 === obj2) // true
두 변수의 reference가 같기 때문에 위 비교는 true가 된다.
useState가 반환하는 set함수는 항상 새로운 객체를 리턴하기 때문에 이전 state 값과 set 이 후 현재 state값의 reference identity가 다르다. 반면 useRef는 직접 mutation을 하기 때문에 렌더링 사이에 같은 reference identity를 유지한다.
let prevRef: any
const App = () => {
const [val, setVal] = useState(0)
const ref = useRef({
count: 0,
})
prevRef = ref
return (
<div>
<button
onClick={() => {
setVal((prev) => prev + 1)
ref.current.count += 1
console.log(prevRef === ref) // true. prevRef와 ref는 같은 reference를 가지고 있다.
}}
>
Render {val}
</button>
</div>
)
}
DOM과 ref
ref를 통해서 DOM 노드를 참조할 수 있다.
<div ref={ref} />
그렇다면 ref를 사용할 수 있는 타이밍은 언제일까?
React는 화면을 그려내는 과정을 두 단계로 나눈다.
- render: 컴포넌트 함수를 호출하여 무엇을 화면에 그려야 하는 지 정한다.
- commit: 결과를 실제 DOM에 반영한다.
const App = () => {
const ref = useRef<HTMLDivElement | null>(null)
console.log('During rendering: ', ref.current) // During rendering: null
useEffect(() => {
console.log('After rendering', ref.current) // After rendering <div></div>
})
return <div ref={ref}></div>
}
위 코드를 보면 알 수 있듯이 ref는 render 단계가 아닌 commit 단계에서 attach 된다.
React sets
ref.current
during the commit. - React docs