setInterval을 react에서 사용하면서 생기는 문제를 만났습니다.
생각하지 못했던 경험이었고 이해하는데 헷갈린 부분이 많아 글로 정리해봤습니다.
코드는 아래와 같습니다.
import React, { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
setInterval(() => {
setCount(count + 1);
}, 1000);
return (
<div>
<p>{`${count}`}</p>
</div>
);
}
export default App;
위 코드를 실행하면 setInterval로 인해 1초에 한번씩 count값이 1씩 올라갈줄 알았지만 생각처럼 실행되지 않았습니다.
위와 같이 숫자가 줄었다 커졌다 하는 현상을 보여줬습니다.
제가 원하는 결과가 아니었기에 왜 이렇게 되는지 생각해봤고
매번의 렌더링마다 setInterval을 추가한다는 것을 파악했습니다.
1초마다 setInterval를 생성하기 때문에 위와 같은 결과가 나옵니다.
또한 count가 0부터 시작한 setInterval을 여러 개 생성하기 때문에 숫자가 줄어드는 현상(count(0)+1이 반복된다)도 발견되는 것이었습니다.
state가 변경될 때만 렌더링을 지원해주는 useEffect를 사용하고자 생각했습니다.
import React, { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
setInterval(() => {
setCount(count + 1);
}, 1000);
}, []);
return (
<div>
<p>{`${count}`}</p>
</div>
);
}
export default App;
이번엔 count가 한번 오르고 멈추는 결과를 보여줬습니다.
계속해서 오르지 않기 때문에 원하는 결과물이 아니었습니다.
이유는 closer의 성격이 있기 때문에 발생하는 문제라고 합니다.
closer에 대해선 따르 글을 올리도록하겠습니다.
closer를 고려한 코드는 아래와같습니다.
import React, { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
setInterval(() => {
setCount((value) => value + 1);
}, 1000);
}, []);
return (
<div>
<p>{`${count}`}</p>
</div>
);
}
export default App;
숫자가 잘 올라가는 것을 확인할 수 있습니다.
위 함수 자체는 setInterval이라는 사이드이펙트를 계속해서 만드는 것이기 때문에 useEffect에서 청소를 해줘야합니다.
아래와 같이 코드를 변경합니다.
import React, { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount((value) => value + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return (
<div>
<p>{`${count}`}</p>
</div>
);
}
export default App;
setInterval을 사용할 때 고려해야할 부분이 많았고 개발할 때 고민해야할 부분이 많기 때문에 custom hook사용을 권장합니다.
import React, { useState, useEffect, useRef } from 'react';
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
custom hook을 사용한 코드는 아래와 같습니다.
따로 hook을 작성하고 useInterval을 사용한다면 고민해야할 것이 줄고 코드량도 확실히 줄일 수 있어 좋은것 같습니다.
import React, { useState, useEffect, useRef } from "react";
function App() {
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 1000);
return (
<div>
<p>{`${count}`}</p>
</div>
);
}
export default App;
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
'React js' 카테고리의 다른 글
값이 변경되어도 렌더링을 하기 싫을 댄 어떻게 해야할까? (feat. useRef) (0) | 2023.01.26 |
---|---|
리액트에서 이벤트 버블링 체험하기 (0) | 2023.01.05 |
React + axios를 사용하여 공공데이터 휴일 호출하고 출력하기 (feat.postman) (0) | 2022.12.06 |
리액트 .env를 이용해 중요한 키를 숨기자 (feat. API key) (0) | 2022.11.30 |
axios get으로 api 호출했는데 response data 깨지는 현상 header 추가 (0) | 2022.11.30 |