React - Hook에서 setInterval setTimeout을 현명하게 사용하는 방법

김동현·2021년 1월 9일
4

React

목록 보기
3/4

문제사항

Hook에서 setInterval()을 사용하면?

  • Function Component에서 Hook을 이용하여 state 관리를 진행한다.
    이때 Vanila Javascript처럼 setInerval()을 받는 함수를 설정하고 특정 조건이 되면 해제하는 형식은 원하는 대로 동작이 이루어지지 않는다.
const [value,setVale] = useState(1);
const [isIncrease,setIsIncrease] = useState(false);
useEffect(()=>{
  	function tick(){
    		return setInterval(()=>setValue(value+1),1000);
    	}
  	return isIncrease ? tick() : ()=>{clearInterval(tick)}; 
},[value,isIncrease])
  • 위와 같이 작성하면, 계속해서 value값은 버벅일 것이다. setValue() 가 끊임없이 발생해서, 원하는대로 setInterval() 이 작동하지 않는다.
  • 기본적으로 React 프로그래밍 모델은 setInterval() 과 부조화가 발생해서 일어나는 일이기도 하다.

해결방법

setTimeout을 이용하자!

  • 기본적으로 반복적으로 계속해서 콜백함수를 호출하는 setInterval() 대신 setTimeout()을 사용하는 것은 현명한 선택이 될 수 있다.
  • 위의 코드를 그럼 다음과 같이 바꾸면 된다.
const [value,setVale] = useState(1);
const [isIncrease,setIsIncrease] = useState(false);
useEffect(()=>{
  	function tick(){
    		return setTimeOut(()=>setValue(value+1),1000);
    	}
  	return isIncrease ? tick() : ()=>{clearInterval(tick)}; 
},[value,isIncrease])
  • 그렇지만 여전히 정상적으로 작동하지는 않는다. isIncreasetrue라 할지라도 tick()에 부여된 Interval를 tick()이 동작한 이후에 없애줘야 하기때문이다.

hook의 return 값으로 clearTimeout() 을 설정하자!

  • 위의 상황을 해결하기 위해선 setTimeout()이 작동하는 useEffect마다 clearTimeout() 을 작동시키는 코드를 return 값으로 넣는 것이다.
const [value,setVale] = useState(1);
const [isIncrease,setIsIncrease] = useState(false);
useEffect(()=>{
  function tick(){
    	return setTimeOut(()=>setValue(value+1),1000);
    }
  if(!isIncrease) return undefined;
  tick();
  return ()=>clearTimeOut(tick);
},[value,isIncrease])
  • Early return 까지 적용되어 훨씬 깔끔한 코드가 되었다.

setTimeOut과 관련된 value만 따로 처리하자!

  • 필자는 다음과 같이 코드를 작성했었다.
useEffect(() => {
    if (gameProcessData.isGameStart) {
        const tick = setInterval(() => {
            setGameData({
                ...gameData,
                time: gameData.time + 1,
            });
        }, 1000);
      	
        return () => clearInterval(tick);
    }
    return undefined;
	}, [gameData, gameProcessData]);
  • gameData 안에서 계속해서 time을 증가시켰는데, 이경우에 gameData의 다른 부분이 변경되도 time을 증가시키는 코드를 동작시키는 것이 문제였다.
  • 따라서 아래와 같이 time 변수를 분리해서 새로운 state로 만들었고 이 부분을 해결했다.
const [time, setTime] : [number, Function] = useState(0);
// 시간 경과 체크를 위한 useEffect
useEffect(() => {
    if (!gameProcessData.isGameStart) return undefined;
    const tick = setTimeout(() => {
        setTime(time + 1);
    }, 1000);

    // eslint-disable-next-line consistent-return
    return () => clearTimeout(tick);
}, [time, gameProcessData]);

참고사이트

profile
Frontend Developer

1개의 댓글

comment-user-thumbnail
2022년 4월 19일

function tick(){
return setTimeOut(()=>setValue(value+1),1000);
}
if(!isIncrease) return undefined;
tick();
return ()=>clearTimeOut(tick);

tick() 호출시 setTimeout 객체가 생성되며 리턴되는데 리턴값을 저장하지않고
clearTimeOut에서 인자로 함수를 지정하는데 js 몇에서 유효한 코드인가요 ??

답글 달기