콘텐츠로 이동

2025 04 15

2025-04-15

Stale Closure

참고: https://handhand.tistory.com/264
참고: https://seongry.github.io/2022/06-05-hooks-dependencies-and-stale-closures/

  • 개요
    • Stale Closure: 상태 값에 변화가 발생해도 감지하지 못하고 예전 값을 바라보는 상태
    • Closure: 생명주기가 끝난 외부함수의 변수를 참조하는 함수
      • 함수는 자신이 선언될 때의 환경 (변수/상수)를 함께 기억
    • useEffect와 같은 훅에서도 의존성 배열에 refresh 해야하는 것을 넘겨주자.
    • 리액트 컴포넌트는 새 props/state로 렌더링시 함수 내부 클로저도 최신 상태로 반영하지만,
      • useCallback/useMemo 등을 쓸 때 의존성 배열 잘 못 관리하면 함수가 계쏙 같은 클로저 사용함
  • state closure 상황
    • 무한 스크롤에서 그 다음 페이지를 불러오지 못하는 문제 발생
    • useCallback에 정의된 함수가 dependency array 항목에 따라 최신화되는데, 최신화되는 조건의 누락시 발생
    • 정의된 함수는 바뀌지 않으니 하염없이 같은 함수만 호출할 수 있음
    • 함수가 생성될 때 사진을 찍는다고 생각하자. 함수 호출은 그냥 사진을 다시 가져오는거고.
  • 코드

    const lastElementRef = useCallback(
        (node: HTMLLIElement) => {
            if (loading) return;
            if (observerHasNext.current) observerHasNext.current.disconnect();
            observerHasNext.current = new IntersectionObserver((entries) => {
                if (entries[0].isIntersecting && hasNext) {
                    getList(service, currentPage + 1, 10, targetType, keywordType, keyword);
                }
            });
            if (node) observerHasNext.current.observe(node);
        },
        [hasNext]
    );
    

    • 다음과 같이 콜백 함수가 hasNext에 대해서만 최신화됨.
      • 이러다 보니 fetch 안의 여러 변수들의 값이 이미 정의된 대로 주구장창 사용중 (stale closure)
      • dependency array에 getList 함수에 들어갈 변수들을 함께 정의해두자.