[React]setInterval
데브 코스/TIL

[React]setInterval

1초마다 1씩 증가하는 autoCounter를 구현해봤다.

 

import { useState, useRef } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  const intervalId = useRef();

  const handleStart = () => {
    intervalId.current = setInterval(() => {
      setCount((count) => count + 1);
      console.log(count);
    }, 1000);
  };

  const handleStop = () => {
    clearInterval(intervalId.current);
  };

  const handleCurrent = () => {
    console.log(count);
  };

  return (
    <div>
      <span>{count}</span>
      <button onClick={handleStart}>start</button>
      <button onClick={handleStop}>end</button>
      <button onClick={handleCurrent}>Current</button>
    </div>
  );
}

export default Counter;

 

근데 화면에는 정상적으로 출력되는데 console.log()는 0에서 움직이지 않는다.

?????

Current 버튼을 눌렀을 때는 정상적으로 출력된다(41, 38인 이유는 캡쳐를 늦게해서 그렇다)

중간에 end를 눌러서 일시정지를 하고 다시 시작하면 멈춤 부분의 숫자가 계속 출력된다.

 왜 그럴까?

React에서 리렌더링이 일어나면, 함수가 새로 실행된다.

 

[번역] 리액트는 언제 컴포넌트를 렌더링 하나요?

정확히 언제 그리고 왜 리액트는 컴포넌트를 렌더링 하는 것일까요? 이 글에서는 리액트 커뮤니티에서 가장 자주 묻는 질문 중 하나인 "리액트는 언제 또는 왜 컴포넌트를 렌더링 하나요?"에 대

velog.io

 우리가 만든 inverval을 해제하고 싶다면 clearInterval을 통해 직접 timer를 해제해야한다.

 

 그렇다면 이 함수는 완벽하게 동작하는가? 

아니다! setInterval 함수는 우리가 원하는 delay시간을 100% 보장하지 못한다. setInterval 함수는 실행하는 시간 조차 delay에 포함시키기 때문에, 만약 함수를 실행하는 시간이 delay보다 길다면 타이머가 제대로 작동하지 않는다. 

 함수가 1초마다 실행되기를 원했는데 함수의 동작시간이 1초보다 길다면 함수의 동작이 끝나고 1초를 기다리지 않고 함수가 다시 실행된다는 뜻이다.

 

 근데 그래도 증가는 해야되는거 아님?

이떄 Closure과 Event Loop 개념을 생각해 보아야 한다.

Closure는 외부 함수가 종료되어도 내부 함수에서 외부 함수의 값을 접근할 수 있다.

setInterval은 종료되었지만 setInterval의 내부 함수인 setCount가 실행될 때마다 초기값 이었던 0을 기억하고 있다.

 setInterval 내부의  console.log()는 초기값인 0을 계속 기억하고 있지만 화면에 출력되는 숫자는 setCount에 콜백함수를 넘겨줘서 업데이트 된 state를 출력하기 때문에 화면에는 정상적으로 표기되지만 console.log()의 값은 0으로 고정되어 있다.

 setCount 내부에도 콜백함수를 사용하지 않고 setCount(count+1)과 같은 방법을 사용한다면 정상적으로 작동하지 않는다.

 React는 리렌더링을 하면서 이전의 render된 내용들을 다 잊고 새로 그리게 되는데, setInterval은 그렇지 않다. Timer를 새로 설정하지 않는 이상 이전의 내용(props나 state)들을 기억하고 있다.

 

해결 방법

useInterval을 사용해보자. 

import { useState, useEffect, useRef } from 'react';

function useInterval(callback, delay) {
  const savedCallback = useRef(); // 최근에 들어온 callback을 저장할 ref를 하나 만든다.

  useEffect(() => {
    savedCallback.current = callback; // callback이 바뀔 때마다 ref를 업데이트 해준다.
  }, [callback]);

  useEffect(() => {
    function tick() {
      savedCallback.current(); // tick이 실행되면 callback 함수를 실행시킨다.
    }
    if (delay !== null) { // 만약 delay가 null이 아니라면 
      let id = setInterval(tick, delay); // delay에 맞추어 interval을 새로 실행시킨다.
      return () => clearInterval(id); // unmount될 때 clearInterval을 해준다.
    }
  }, [delay]); // delay가 바뀔 때마다 새로 실행된다.
}

 

잘 늘어나긴 하는데...onClick과 Hook을 연결하는 방법??

 

 

 

 

 

React에서 setInterval 현명하게 사용하기(feat. useInterval)

들어가기 Babble의 방 목록 페이지에 들어가면 유저가 생성한 방들이 쭉 나열되어 있는 것을 볼 수 있다. (안타깝게도 유저가 없으면 방도 없다ㅜㅜ) 그리고 이 방들은 5초마다 서버에 요청을 보내

mingule.tistory.com

 

 

useInterval() react hook - usehooks-ts

Use setInterval in functional React component with the same API. Set your callback function as a first parameter and a delay (in milliseconds) for the…

usehooks-ts.com

 

'데브 코스 > TIL' 카테고리의 다른 글

[TIL] TDZ  (0) 2022.12.20
화살표 함수  (0) 2022.12.20
프로토타입  (0) 2022.12.20
[회고]노션 클로닝.  (0) 2022.11.25
[TIL]선언형 프로그래밍.  (0) 2022.10.28