... const [isLastPage, setIsLastPage] = useState(false); const loadMoreBtn = useRef<HTMLSpanElement>(null); const io = new IntersectionObserver( async (entries) => { const element = entries[0]; if (element.isIntersecting) { await onLoadMore(); } }, { threshold: 1, }, ); const onLoadMore = async () => { //observer의 콜백함수에서 호출하는 함수 if (isLastPage) { //처음 상태값(false)을 캡쳐해서 계속 false이다. return; } ... if (!products.length) { setIsLastPage(true); //해도 콜백함수에선 소용 없음 } ... }; useEffect(() => { if (loadMoreBtn.current) { io.observe(loadMoreBtn.current); } return () => io.disconnect(); }, []);
문제의 원인
React 상태값은 렌더링마다 새로운 값으로 갱신되지만, IntersectionObserver의 콜백은 해당 함수가 생성될 당시의 렌더링된 상태를 캡쳐해서 계속 참조한다.
따라서, 이후 상태가 업데이트되더라도 콜백 함수 내부에서는 이전 상태값을 계속 참조해서 생긴 문제!
해결방법
방법1. useEffect의 의존값을 상태로 지정하고, 이 내부에서 IntersectionObserver를 생성한다.
⇒ 상태가 업데이트될 때마다 IntersectionObserver가, 즉 콜백이 새롭게 업데이트된다.
useEffect(() => { const io = new IntersectionObserver( ... ); //io를 useEffect에서 선언해서 밑에 코드도 같이 여기에 넣어줌! if (loadMoreBtn.current) { io.observe(loadMoreBtn.current); } return () => io.disconnect(); }, [page]);
방법2. state 값을 담은 ref를 쓴다.
⇒ state가 업데이트 될 때마다 ref값도 업데이트 시킨다.
const [isLastPage, setIsLastPage] = useState(false); const isLastPageRef = useRef(isLastPage); useEffect(()=>{ isLastPageRef.current = isLastPage //2. ref값 업데이트 },[isLastPage]) const onLoadMore = async () => { if (isLastPage.current) { //current는 업데이트 됨 ㅇㅇ return; } ... if (!products.length) { setIsLastPage(true); // 1. 상태값을 업데이트하면 } ... };