[React] 클로저 트랩 해결하기 (feat. useEffet)

문정민·2024년 1월 6일

React

목록 보기
9/11
post-thumbnail

리액트로 프로젝트나 과제를 하다가 종종 겪었던 클로저 트랩에 대해서 정리해보고자 한다.

📝 클로저의 특성

클로저 트랩을 알아보기 이전에 먼저 클로저의 특성에 대해서 이해해야 한다. 클로저는 내부 함수가 외부 함수의 변수에 접근할 수 있도록 해준다. 외부 함수의 생명 주기가 끝났다고 해도 내부 함수에서 외부 함수의 변수를 참조하고 있다면 이 변수에 접근하여 사용할 수 있다.

이때 내부 함수는 함수가 정의될 때의 변수를 기억한다. 즉, 외부 함수의 변수가 업데이트가 되어도 내부 함수에서는 변수의 처음 상태로 기억을 한다.

📝 클로저 트랩이란?

클로저 트랩은 클로저가 외부 함수의 변수를 '캡처'할 때 발생하는 문제이다. 앞서 언급했듯이 클로저는 함수가 정의될 때의 렉시컬 환경을 기억하고, 이 환경에 있는 변수들에 접근할 수 있다. 이로 인하여 함수가 최신 상태의 변수를 참조하지 않고, 함수가 생성될 당시의 상태를 참조하는 문제가 발생할 수 있다.

UseEffect와 클로저 트랩

useEffect의 콜백함수는 클로저로 작동한다. useEffect의 콜백 함수는 함수형 컴포넌트 내부에서 선언된다. 아래의 예시에서 useEffect의 콜백 함수는 내부 함수이고, 외부 함수인 Example의 변수인 count를 참조하고 있는 것이다. 콜백 함수가 정의될 때의 count의 상태를 '기억'하게 되고, count의 값은 0이 찍힌다.

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

  useEffect(() => {
    setCount(1);
    console.log(count);
  }, []);
};

이를 해결하는 방법은 간단하다. 의존성 배열을 잘 넣어주면 된다. 콜백 함수 내에서 사용되는 상태와 속성을 포함시키면 (위의 경우에는 count를 의존성 배열에 넣어준다.) 상태나 속성이 변경될 때마다 useEffect 콜백이 재실행 되면서 최신 값을 반영하게 된다.

📝 내가 겪은 클로저 트랩

내가 겪은 문제는 나의 실수 + 클로저 트랩이 함께 발생한 경우였다.

문제점

  1. tableInstance는 업데이트가 되지 않는다. 따라서, 의존성배열에 넣어도 currentItems는업데이트가 되지 않는다.
  2. handleRegister는 함수가 정의될 때인 currentItems의 초기값을 기억하기 때문에, currentItems는 빈배열로 출력된다.
const List = ({ tableInstance }) => {
  const [currentItems, setCurrentItems] = useState([]);
  
  useEffect(() => {
    if (tableInstance) {
      const newRows = tableInstance.getRowModel().rows;
      setCurrentItems(newRows);
    }
  }, [tableInstance]);
  
  const handleRegister = () => {
      // 중략
      } else {
        console.log("handleRegister", currentItems);
      }
    };
    return (
      <div>
        <button onClick={handleRegister}/>
      </div>
    );
  }; 

해결 방법

상태를 useEffect의 콜백 함수에서 업데이트하는 것이 아니라 handleRegister 안에서 직접 선언하여 사용하도록 수정했다.


const handleRegister = () => {
    // 중략
  } else {
    const currentItems = tableInstance.getRowModel().rows;
    console.log("handleRegister", currentItems);
  }};

0개의 댓글