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의 상태가 유지되지 않고 초기화 된다는 것을 알 수 있습니다.
'dev' 카테고리의 다른 글
[코드숨 리액트 강의] 5주차 회고 - 비동기 코드에서 Thunk를 쓰는 이유 (0) | 2022.06.06 |
---|---|
"나는 LINE 개발자입니다"를 읽고 (0) | 2021.09.12 |
AI 이미지 캡셔닝 - Image Captioning (0) | 2020.12.16 |
딥러닝 모델의 Overffiting을 줄이는 방법 (0) | 2020.12.15 |
머신 러닝 첫 걸음 (1) | 2020.12.15 |
댓글