.
본문 바로가기
리액트

React에서 setInterval 작성하기

by 와칸다개발자 2021. 11. 22.

리액트 에서 setInterval을 작성해 봅시다.

 

오래전에 구현하느라 애먹었는데 이제야 글을 쓰게 되는군요....

 

간단하게 코드를 보자면

 

  import { useState } from "react";
  import "./styles.css";

  export default function App() {

    const[cnt, setCnt] = useState(0);
    const run = () => {
      setInterval(() => {
        setCnt(prev => prev + 1);
      }, 1000);
    }

    return (
      <div className="App">
        <h1>{cnt}</h1>
        <button onClick={run}>run</button>
      </div>
    );
  }

 

버튼을 클릭할 시 count를 1씩 1초마다 증가시킵니다.

 

잘 작동됩니다. 

 

하지만... 버튼을 계속 클릭하게 되면 숫자 카운트는 빠르게 증가합니다. 

 

 

대체 왜 그럴까요?

 

run 버튼을 클릭할 때 마다 기존에 작성했던 setInterval 함수는 clear 되지 않고 그대로 있기 때문입니다.

 

아직 clear 되지않는 여러개의 sestInterval 함수는 카운트를 1초마다 증가시키지만 여러개가 있으니 

 

이렇게 보이게 되는 것입니다. 한번 다르게 작성해 봅시다.

 

import { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [cnt, setCnt] = useState(0);
  let setIntervalID;

  useEffect(() => {
    return () => clearInterval(setIntervalID);
  }, [setIntervalID]);

  const run = () => {

    if(setIntervalID !== undefined)
      return;

    setIntervalID = setInterval(() => {
      setCnt((prev) => prev + 1);
    }, 1000);
  };

  return (
    <div className="App">
      <h1>{cnt}</h1>
      <button onClick={run}>run</button>
    </div>
  );
}

 

컴포넌트가 언마운트 될 때 intervalID를 clear 해줬습니다. 또 버튼클릭 이벤트 발생 시 조건문을 걸어서

 

리턴해주도록 했습니다. 

 

네 결과는 똑같습니다. 

 

올바르게 작성하기 위해서는 'useRef'를 사용해야 합니다. 

 

  import { useEffect, useState, useRef } from "react";
  import "./styles.css";

  export default function App() {
    const [cnt, setCnt] = useState(0);
    const timerId = useRef(null);

    useEffect(() => {
      return () => clearInterval(timerId);
    }, [timerId]);

    const run = () => {
      if (timerId.current !== null) return;

      timerId.current = setInterval(() => {
        setCnt((prev) => prev + 1);
      }, 1000);
    };

    const stop = () => {
      clearInterval(timerId.current);
      timerId.current = null;
    }

    return (
      <div className="App">
        <h1>{cnt}</h1>
        <button onClick={run}>run</button>
        <button onClick={stop}>stop</button>
      </div>
    );
  }

useRef는 값이 변한다 하더라도 리액트는 렌더링 하지 않습니다. 

 

useRef의 current 데이터는 컴포넌트라 언마운트 되던 그대로 메모리에 존재하므로 

 

매번 run 버튼을 클릭하더라도 값이 그대로기에 새로운 interval id를 할당하지 않습니다.

댓글