본문 바로가기
  • 하고 싶은 일을 하자
dev

React에서 setInterval 제대로 쓰는 방법

by 박빵떡 2021. 7. 29.
반응형

React에서 setInterval을 사용하는 방법은 Vanila Javascript와는 다릅니다.

아래의 예시 코드를 보시죠.

 

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

export default function App() {
  const [number, setNumber] = useState(0);

  const loop = setInterval(() => {
    console.log("number", number);
    setNumber(number + 1);
    if (number === 10) clearInterval(loop);
  }, 1000);

  return <div className="App">number : {number}</div>;
}

10초를 센 뒤에 루프가 끝나는 코드로 동작할 것 같지만 우리가 원하는 대로 코드는 동작하지 않습니다. 그 이유는 setInterval에 의해 number가 바뀌어서 App이 리렌더링되는데 setInterval도 같이 렌더링 되기 때문입니다. console을 보면 number가 무한히 찍히는 걸 보실 수 있습니다.

샌드 박스 결과 보기

 

이를 해결하는 방법은 useEffect를 활용해 App 컴포넌트가 처음 렌더링 될 때만 setInterval이 렌더링 되도록 하는 방법을 사용하면 됩니다.

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

export default function App() {
  const [number, setNumber] = useState(0);

  useEffect(() => {
    const loop = setInterval(() => {
      setNumber((prev) => prev + 1);
      console.log("number", number);
      if (number === 10) clearInterval(loop);
    }, 1000);
  }, []);

  return <div className="App">number : {number}</div>;
}

샌드박스 결과 보기

 

하지만 이 방법도 문제가 있습니다. setInterval 내부에서 number를 출력해보면 항상 0을 출력하고 있습니다. 즉 App이 리렌더링되면서 number의 초기 상태인 0이 계속 리렌더링 되고 있기 때문입니다. 그래서 clearInterval이 실행되지 못합니다. 이를 해결하는 방법은 useRef를 사용하는 것입니다.

 

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

export default function App() {
  const [number, setNumber] = useState(0);
  const number_ref = useRef(0);

  useEffect(() => {
    const loop = setInterval(() => {
      number_ref.current += 1;
      setNumber(number_ref.current);
      console.log("number", number);
      if (number_ref.current === 10) clearInterval(loop);
    }, 1000);
  }, []);

  return <div className="App">number : {number}</div>;
}

샌드박스 결과 보기

 

샌드박스 결과를 보면 우리가 원하는대로 동작하는 것을 알 수 있습니다. 흥미로운 것은 setInterval 내부의 number를 출력해보면 number 값은 항상 0인데 <div> 태그 안의 number는 증가하는 숫자로 표시된다는 점입니다. 이를 통해서 setInterval 로 인해 App 이 리렌더링 되면서 number는 useState(0)의 초기값으로 지정되고, 컴포넌트가 언마운트 되기 전까지 그 값이 유지되는 number_ref는 1씩 증가하며, setNumber(number_ref.current)에 의해 return문에서 렌더링되는 number는 증가한 숫자임을 알 수 있습니다.

 

기존의 useState는 컴포넌트가 리렌더링 되더라도 이전의 값이 유지됩니다.

import React, { useState } from 'react';

function Counter() {
  const [number, setNumber] = useState(0);
  console.log('number', number);

  const onIncrease = () => {
    setNumber(prevNumber => prevNumber + 1);
  }

  const onDecrease = () => {
    setNumber(prevNumber => prevNumber - 1);
  }

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

샌드박스 결과 보기

즉 setInterval로 리렌더링 되는 컴포넌트는 useState의 상태가 유지되지 않고 초기화 된다는 것을 알 수 있습니다.

 

반응형

댓글