주의: 정확하지 않은 서술이 있을 수 있습니다.
컴포넌트 A에서 버튼을 클릭하면 recoil state가 변경되고, Hook B의 useEffect의 의존성 배열에 해당 recoil state가 있어서 Hook B에서 동작이 수행되게 설계를 했었다.
하지만 Hook B에서 recoil state의 변경을 잘 감지하지 못하는 경우가 발생했고, 따라서 의도치 않게 작동이 되는 상황이 벌어졌다.
1. 왜 recoil state의 변경을 감지하지 못했을까?
// Component A
const [something, setSomething] = useRecoilState(somethingState);
const handleItemClick = (e: React.MouseEvent) => {
setSomething({A : newValue});
}
(...)
<Button onClick={handleItemClick />
// Hook B
const something = useRecoilValue(somethingState);
useEffect(() => {
// do something with something
}, [something])
위와 같이 구성된 상황에서, 가끔씩 Hook B는 Component A의 변경된 상태가 아닌 이전 상태를 인식했다.
왜냐하면 Recoil은 setSomething을 호출하면 상태 변경을 요청하는데, 이 상태 변경은 다음 렌더링 사이클에서 실제 반영한다.
하지만 이 때, Hook B의 useEffect는 이전 렌더링 시점의 클로저를 캡처할 수 있기 때문에 상태 변경이 안되는 경우가 발생한다.
→ 이 현상을 바로 stale closure라고 한다.
2. useRef를 통해서 최신 상태를 참조하자
const something = useRecoilValue(somethingState);
const somethingRef = useRef(something);
useEffect(() => {
somethingRef.current = something;
}, [something]);
useEffect(() => {
// do something with somethingRef.current
}, [something])
한 파일에 의존성 배열이 같은 useEffect가 있을 때 위에 있는 useEffect부터 실행된다(주의: 100% 보장은 아니라고한다).
더 위에 있는 useEffect를 통해 새로운 something을 somethingRef.current에 저장하고, 두 번째 useEffect(즉, 기존의 useEffect)에서 somethingRef.current를 통해서 갱신된 something 값을 사용하면 오류없이 작동을 보장할 수 있다.
주의: useEffect를 사용하지 않고 somethingRef.current = something;을 적용하는게 더 정확하다는 의견이 있다. 왜냐하면 직접 할당을 하면 렌더링 때마다 somethingRef.current에 something을 할당하기 때문에 더 안전하기 때문이라고 한다.
'Intern' 카테고리의 다른 글
배경이 따라오는 메모장을 만들기 위해 contentEditable을 사용해보자 (0) | 2025.04.19 |
---|---|
모노레포에서 다른 프로젝트로 파일을 옮길 때 조심해야 할 점 (0) | 2025.04.19 |
네이버 지도 api 사용 시 마커가 많을 때 최적화 후기 (1) | 2025.03.15 |