Hooks를 이용해 개발하던 도중 일반 변수(variable)와 useRef의 차이점에 대해 궁금해져서 공부를 한 내용을 정리해보자 한다.
function Counter() {
const [counter, setCounter] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCounter(counter => counter + 1);
}, 1000);
return () => {
clearInterval(timer);
alert(counter);
};
}, []);
return (
<div>
<p>{counter}</p>
</div>
);
}
위의 코드대로 작성하면 counter가 1초마다 증가하는 rendering은 정상적으로 나타나지만 alert은 항상 0으로 출력된다.
useEffect의 dependency에 counter가 의존 되지 않는 상태이기에 아래 같이 변경해줘야 한다.
useEffect(() => {
const timer = setInterval(() => {
setCounter(counter => counter + 1);
}, 1000);
return () => {
clearInterval(timer);
alert(counter);
};
}, [counter]);
하지만 위의 코드대로 수정하면 이렇게 만들어버리면 counter가 변할 때마다 useEffect가 trigger되니까 원래 로직이랑 달라지게 되며 이러한 현상은 useState가 closure 안의 값까지 업데이트해줄 수는 없는 것으로 설명된다.
위의 useState를 이용한 코드를 보면 counter가 변할 때 마다 렌더를 하고 싶은 경우
const value = useRef(0);
const [count, setCount] = useState(value.current);
위의 코드를 사용해서 적용하고
매번 렌더할 필요가 없는 아래와 같은 경우는
const counter = useRef(0)
이렇게 작성하여 코딩을 하면 될 것이다.
위의 useState를 사용한 예시 코드는 굳이 리렌더를 할 필요가 없기에
후자 코드인 const counter = useRef(0)를 사용하면 될 것 같다.
function CounterKai() {
const counter = useRef(0);
useEffect(() => {
const timer = setInterval(() => {
counter.current += 1;
}, 1000);
return () => {
clearInterval(timer);
alert("<CounterKai/>:", counter.current);
};
}, []);
return (
<div>
<p>{counter.current}</p>
</div>
);
}
import React, { useState, useEffect, useRef } from 'react';
// defined a variable outside function component
let countCache = 0;
function Counter() {
const [count, setCount] = useState(0);
countCache = count; // set default value
useEffect(() => {
setTimeout(() => {
// We can get the latest count here
console.log(`You clicked ${countCache} times (countCache)`);
}, 3000);
});
// ...
}
export default Counter;
function Example() {
const [count, setCount] = useState(0);
const latestCount = useRef(count);
useEffect(() => {
// Set the mutable latest value
latestCount.current = count;
setTimeout(() => {
// Read the mutable latest value
console.log(`You clicked ${latestCount.current} times`);
}, 3000);
});
// ...
}
useRef는 공식문서에 의하면 아래와 같이 정의 된다.
useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환합니다. 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지될 것입니다.
이 기능은 클래스에서 인스턴스 필드를 사용하는 방법과 유사한 어떤 가변값을 유지하는 데에 편리합니다.
위의 문장처럼 useRef는 컴포넌트의 전 생명주기를 통해 유지되는 값이라는 의미이며, 순수한 자바스크립트 객체를 생성 및 유지시켜주기 때문에 closure 이슈가 발생하지 않는다는 걸 알 수 있다