100ms마다 시간을 업데이트하려고 하기 때문에
아래와 같이 지정한 주기마다 함수를 실행하는 방법을 찾았습니다.
setInterval | setTimeout |
---|---|
함수의 실행시간은 반복주기에 영향을 주지 않는다. | 실행한 함수가 종료된 이후부터 다시 지정한 시간 간격까지 기다린다. |
setInterval
setInterval(실행할함수, 100);
여기서 실행할함수는 300ms 소요된다.
setInterval 메서드는 100ms마다 실행할함수를 task queue에 넣지만 실행할함수는 300ms가 소요되기 때문에
실제로는 300ms마다 함수가 반복되는걸 확인할 수 있다.
이처럼 100ms마다 task queue에 쌓이지만 300ms마다 task queue에서 꺼내서 실행하기 때문에
task queue에는 많은 task가 쌓이게 되고 STOP버튼을 눌렀을때 바로 실행되지 못하는 문제가 있었습니다.
setTimeout
setTimeout 메서드 내부에선 업데이트된 state값에 접근할 수 없는 문제가 있었는데 해당 블로그의 해결방법이 있었습니다.
출처: https://upmostly.com/tutorials/settimeout-in-react-components-using-hooks
일정 시간이 지난 후 함수나 코드 블록을 실행하려면 React 컴포넌트에서 setTimeout 을 사용하면 됩니다.
React에서 setTimeout을 사용하는 방법을 살펴보겠습니다.
setTimeout 메서드는 두 번째 인수를 사용해서 지정한 일정 시간이 지난 후 함수를 호출하거나 일부 코드를 실행합니다.
React 컴포넌트 내에서 setTimeout 을 사용하는 것은 일반 자바스크립트 메서드이기 때문에 충분히 쉽습니다.
예를들어, Hook을 사용하는 함수형 리액트 컴포넌트 내부에서 setTimeout 을 사용해보겠습니다.
클래스 컴포넌트의 componentDidMount 생명주기 메서드에 해당하는 useEffect Hook 내부에서 setTimeout을 호출할 것입니다.
const React, { useEffect } from 'react'
const App = () => {
useEffect(()=>{
const timer = setTimeout(()=>{
console.log('1초뒤 실행된다');
},1000);
return () => clearTimeout(timer);
},[]);
return null;
}
App 컴포넌트가 처음 마운트될 때 timer라는 새로운 setTimeout 을 예약합니다.
결과적으로 setTimeout 메서드의 두 번째 매개변수로 전달된 1000ms 값에 표시된 대로 1초후에 setTimeout 블록 내부의 코드가 실행됩니다.
그렇지 않으면, 코드에서 부작용이 발생할 수 있기 때문에 setTimeout timer를 지우고 올바르게 처리해야 합니다.
timer를 지우거나 취소하려면 clearTimeout
메서드에 timer 객체를 전달해서 호출해야 합니다.
setTimeout 메서드 내부에선 해당 state의 현재값이 사용되지 않습니다.
setTimeout 메서드 내부의 state에 접근하려고 하면 setTimeout 메서드 및 state와 관련해서 이상한 문제를 발견했습니다.
import React, { useEffect, useState } from 'react';
const App = () => {
const [count, setCount] = useState(0);
const [countInTimeout, setCountInTimeout] = useState(0);
useEffect(()=>{
setTimeout(()=>{
// 여기서 카운트는 0이다.
setCountInTimeout(count);
},3000);
// setTimeout 이 예약된 후 count state를 5로 업데이트한다.
setCount(5);
},[]);
return null;
}
countInTimeout state값은 5로 예상했지만, 실제로는 0이 됩니다.
setTimeout은 클로저이기 때문에 setTimeout이 예약될 때 정확한 시점의 카운트 값, 즉 초기값인 0을 사용합니다.
이러한 문제를 해결하기 위해서는 useRef Hook을 사용하면 됩니다.
const App = () => {
const [count, setCount] = useState(0);
const [countInTimeout, setCountInTimeout] = useState(0);
const countRef = useRef(count);
countRef.current = count;
const getCountTimeout = () => {
setTimeout(()=>{
setCountInTimeout(countRef.current);
},3000);
}
}
이 솔루션은 해당 깃허브의 토론 덕분입니다.
setTimeout
내부에서 state
가 업데이트되지 않습니다.setTimeout 이 예약될 때 예약된 시점의 state값을 사용합니다.
클로저에 의존해서 비동기적으로 카운트에 접근합니다.
컴포넌트가 리렌더링되면 새로운 클로저가 생성되지만 처음에 닫힌 값은 변경되지 않습니다.
setTimeout
메서드 내부에서 업데이트된 state
에 접근하려면??업데이트된 state를 쓰고, 이후에 timeout되었을 때 읽을 수 있는 container 를 사용해야하는데, 이는 useRef의 활용법 중 하나입니다.
state값을 ref.current와 동기화하면 timeout에서 업데이트된 state값을 읽을 수 있습니다.
// 비동기콜백에서 현재 state값에 접근하려면 ref를 사용합니다.
const countRef = useRef(count)
countRef.current = count
const getCountTimeout = () => {
setTimeout(()=>{
setCountInTimeout(countRef.current)
},2000)
}