React useInterval

김대은·2022년 8월 21일
0

리액트 훅스 컴포넌트에서 setInterval 사용 시의 문제점
React Hooks에서 setInterval() 사용 문제

본 게시글은 위 링크를 바탕으로 작성되었습니다.
useInterval의 사용법을 익히기 위하여 기록합니다.

useInterval hooks

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]);
}

useRef의 활용

const savedCallback = useRef();

useInterval훅을 보면 callback 함수와 delay를 전달받아서
useEffect 내부에서 callback 함수를 savedCallback.current에 저장한다.

이때, useRef훅을 사용한 이유는 리랜더링을 방지하기 위함이다.
useRef 는 함수형 프로그래밍에서 사용하는 ref로 초기화된 ref 객체인 {current:null}을 반환하며 , 반환된 객체는 컴포넌트의 전 생애주기 동안 유지되어 useRef로 관리되는 값은 변경되더라도 컴포터는가 리 랜더링 되지 않는다.

만약 useState를 사용했더라면?

const [count, setCount] = useState(0);

useInterval(()=>{
  setCount(count => count+1);
}, 1000);

useState를 사용하여 데이터를 관리할 경우 값이 변경될 때 리렌더링이 일어나기 때문에 useEffect() 내부에서 savedCallback 값이 변경될 때마다 리렌더링이 일어나게 되고,
이 때문에 두 번째 useEffect() 내부에서 savedCallback 값을 확인하면 계속해서 초기값만을 가져오게 될 것이다.

callback 함수를 저장하는 useEffect hooks

useEffect(() => {
  savedCallback.current = callback;
}, [callback]);

위 코드를 보면 callback데이터가 변경될 때마다 useEffect가 실행되어 savedCallback의 current 값이 새로운 callback 데이터로 업데이트된다.

🤔 왜 useInterval()에 함수를 전달해서 내부에서 값을 업데이트 해주는 것일까?

  • useEffect는 의존성 배열을 전달하지 않으면 리렌더링 될때마다 실행된다.
  • useEffect의 두번째 인자에 빈 배열을 전달할 경우 첫 렌더링 시에만 실행되어 훅 내부에서는 변화된 데이터 값을 얻을 수 없다.
  • useEffect는 특정 데이터가 변경될때 마다 실행되도록 할 수 있으나,
    useEffect내부에서 값이 업데이트 되는 데이터에 의존할 경우 무한 루프에 빠지게 된다.

이를 해결하고자 useInterval훅은 리렌더링이 될 때마다 실행되어 업데이트 된 데이터를 가질수 있게 만들고, 데이터를 업데이트 하는 함수를 useInterval훅에 전달하여 내부에서 savedCallback.current에 저장하여 새로 업데이트 된 데이터 값을 useEffect훅 내부에서도 얻을 수 있도록 한것이다.

setInterval을 호출하는 useEffect hooks

첫 번째 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 될 때 해당 함수가 실행되어 불필요한 동작을 제거하거나 메모리 누수 문제를 방지할 수 있어 사용한다.

profile
매일 1% 이상 씩 성장하기

0개의 댓글