버스 페이지 개발일지 - [2] React useInterval 시 재 렌더링 최소화

최원빈·2022년 10월 17일
0


컨테이너 + 프레젠테이션 구조로 리액트 앱을 짜면, 컨테이너 컴포넌트에서 Hook을 통해 state를 관리한다.

이번에는 React Hook의 기초 내용을 이해하게 해준 '얼마나 남았나' 를 표기해주는 info 파트의 제작일지이다.

버스 카드 내에 있는 '얼마나 남았나'를 보여주는 문구의 갱신 setBusTime() 함수는 세 가지 상황에 호출되어야 한다.

  1. 생성 시
  2. 1초마다
  3. 상단의 출발지 + 목적지를 바꾸는 드롭다운 state의 갱신

제작 과정은 이러했다.

  1. 생성 시에 갱신하면 되니 useEffect()안에 setBusTime()을 넣자.
  2. 1초마다 갱신해야 하니 setBusTime함수 내에 window.setInterval(logic, 1000)을 넣자.
  3. useEffect에 넣어놨으니 출발지 + 목적지 갱신하면 알아서 호출될것이다.

실제 동작은 이러했다.

  1. 생성 시 갱신은 성공
  2. 매 랜더링마다 새로운 useEffect가 실행되고, setBusTime이 반복적으로 실행되므로 setBusTime은 실행 후 매 초의 제곱만큼 실행되었다.
  3. 출발지 + 목적지 갱신 시 호출도 성공
    2번의 문제는 setInterval함수를 랜더링마다 실행하게 되므로 당연한 결과였다.

setInterval함수를 컨테이너 밖으로 꺼낸다 하더라도 매 렌더링은 고유의 값들을 가지므로 destroyed, 즉 componentWillUnmount에 clearInterval을 넣어야 하므로 이 과정을 통합하여 custom Hook을 이용했다.

import React, { useRef, useEffect } from "react"

useInterval(() => {
    setBusTime(params);
},1000)

//hoisting
function useInterval(callback, delay) {
    const savedCallback = useRef();

    // Remember the latest function.
    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]);

이제 실행을 했을 때, 다음 문제가 발생했다. 현재 useEffect의 상태를 보자.

 useEffect(() => {
    setBusTime(params);
})

매 초마다 useInterval()에 의해 setBusTime()이 실행되지만, setBusTime()에 의해 화면이 재 렌더링되면서 useEffect에 의해 한번 더 setBusTime이 호출된다.

그렇기에 useEffect에 의존성을 부여할 필요가 생긴다. setBusTime은 렌더링될 때, departList와 arrivalList가 바뀔 때만 호출되어야한다.

useEffect(() => {
    setBusTime(params);
}, [departList, arrivalList]);

이제 setBusTime은 알맞게 호출된다.
useEffect에 의해 렌더링될 때 호출,
useInterval에 의해 매 초마다 호출,
드롭다운에 의해 departList와 arrivalList가 바뀔 때마다 useEffect에 의해 호출.

profile
FrontEnd Developer

0개의 댓글