thumbnail-maker_202537.jpg

useEffectEvent ?

React 공식 문서의 Separating Events from Effects 섹션에서는 useEffectEvent에 대해 설명하고 있습니다. "event와 effect를 분리한다"는 개념이 다소 낯설게 느껴질 수 있는데, 간단한 예시를 통해 자세히 알아보겠습니다.

useTimeout Hook 구현

먼저, useTimeout이라는 hook을 구현해 보겠습니다. 이 hook은 지정된 시간(밀리초) 후에 콜백 함수를 실행해줍니다.

::$SPACE

아래는 기본적인 useTimeout 구현 코드입니다.

const useTimeout = (callback: () => void, delay: number) => {
  const timer = useRef<NodeJS.Timeout | null>(null);

  const start = useCallback(() => {
    timer.current = setTimeout(() => {
      callback();
    }, delay);
  }, [callback, delay]);

  useEffect(() => {
    return () => {
      if (timer.current) clearTimeout(timer.current);
    };
  }, []);

  return useMemo(() => ({ start }), [start]);
};

이제 이 hook을 사용해 보겠습니다.

export const App = () => {
  const { start } = useTimeout(() => {
    console.log('time out !!');
  }, 3000);

  return (
    <main>
      <button onClick={start}>시작</button>
    </main>
  );
};

시작 버튼을 누르고 3초를 기다리면

스크린샷 2025-03-07 오후 10.14.23.png

time out !! 이 출력됩니다. 이 경우, 코드는 정상적으로 동작합니다.

라고 생각하면 안됩니다.

::$SPACE

어떤 문제가 있을까요? 사용 예제를 다음과 같이 수정해 보겠습니다.

export const App = () => {
  const [count, setCount] = useState(0);

  const { start } = useTimeout(() => {
    console.log(`time out !! 현재 카운트: ${count}`);
  }, 3000);

  return (
    <main>
      <button onClick={start}>시작</button>
      <button onClick={() => setCount(count + 1)}>증가 (현재: {count})</button>
    </main>
  );
};

이제 시작 버튼을 클릭한 후, count 증가 버튼을 여러 번 누르면

스크린샷 2025-03-07 오후 10.19.28.png

총 10번 정도 눌렀음에도 불구하고