리액트 훅스 컴포넌트에서 setInterval 사용 시의 문제점
React Hooks에서 setInterval() 사용 문제
본 게시글은 위 링크를 바탕으로 작성되었습니다.
useInterval의 사용법을 익히기 위하여 기록합니다.
function useInterval(callback, delay) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
const savedCallback = useRef();
useInterval훅을 보면 callback 함수와 delay를 전달받아서
useEffect 내부에서 callback 함수를 savedCallback.current에 저장한다.
이때, useRef훅을 사용한 이유는 리랜더링을 방지하기 위함이다.
useRef 는 함수형 프로그래밍에서 사용하는 ref로 초기화된 ref 객체인 {current:null}을 반환하며 , 반환된 객체는 컴포넌트의 전 생애주기 동안 유지되어 useRef로 관리되는 값은 변경되더라도 컴포터는가 리 랜더링 되지 않는다.
const [count, setCount] = useState(0);
useInterval(()=>{
setCount(count => count+1);
}, 1000);
useState를 사용하여 데이터를 관리할 경우 값이 변경될 때 리렌더링이 일어나기 때문에 useEffect() 내부에서 savedCallback 값이 변경될 때마다 리렌더링이 일어나게 되고,
이 때문에 두 번째 useEffect() 내부에서 savedCallback 값을 확인하면 계속해서 초기값만을 가져오게 될 것이다.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
위 코드를 보면 callback데이터가 변경될 때마다 useEffect가 실행되어 savedCallback의 current 값이 새로운 callback 데이터로 업데이트된다.
🤔 왜 useInterval()에 함수를 전달해서 내부에서 값을 업데이트 해주는 것일까?
useEffect는 의존성 배열을 전달하지 않으면 리렌더링 될때마다 실행된다.useEffect의 두번째 인자에 빈 배열을 전달할 경우 첫 렌더링 시에만 실행되어 훅 내부에서는 변화된 데이터 값을 얻을 수 없다.useEffect는 특정 데이터가 변경될때 마다 실행되도록 할 수 있으나,useEffect내부에서 값이 업데이트 되는 데이터에 의존할 경우 무한 루프에 빠지게 된다.이를 해결하고자 useInterval훅은 리렌더링이 될 때마다 실행되어 업데이트 된 데이터를 가질수 있게 만들고, 데이터를 업데이트 하는 함수를 useInterval훅에 전달하여 내부에서 savedCallback.current에 저장하여 새로 업데이트 된 데이터 값을 useEffect훅 내부에서도 얻을 수 있도록 한것이다.
첫 번째 useEffect훅을 통해 callback 함수를 저장했으면, 두 번째 useEffect에서는 setInterval함수에 해당 callback 함수를 전달해 실행되도록 만들어야한다.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
위의 useEffect는 delay가 변경될 때마다 실행되며 delay가 null값이 아닐 경우에 setInterval함수를 호출하여 callback 함수를 실행한다.
이렇게 작성하면
useEffect가 무한히 실행되는것을 방지하며 delay가 변경될 때에만 타이머를 재실행하게 된다.useEffect는 콜백함수가 변경될때마다 업데이트하기 때문에 결국 두번째 useEffect 내의 setInterval()함수는 재 실행되지 않고도 새로 업데이트 된 콜백함수를 실행할 수 있다.이 때 delay를 null check 하는 이유는 Interval을 일시중지 할 수 있게 하기 위해서이며 useInterval에 null인 delay를 전달할 경우 더 이상 setInterval 함수가 실행되지 않게 된다.
return () => clearInterval(id); 부분은 clean-up 함수로 class component의 경우는 componentWillUnmount라는 라이프사이클 메서드를 이용해 구현하며 funtion component의 경우는 useEffect()에 전달한 함수의 return 함수로 구현한다.
useEffect() 내에서 함수를 반환하면 컴포넌트가 unmount 될 때 해당 함수가 실행되어 불필요한 동작을 제거하거나 메모리 누수 문제를 방지할 수 있어 사용한다.