나만보는개발공부블로그

Scroll restoration in Nextjs 본문

Web Development/Front

Scroll restoration in Nextjs

alexrider94 2022. 9. 17. 12:49

Nextjs에서의 스크롤 유지 방법


Nextjs에는 페이지 사이를 이동할 때 스크롤 높이를 유지하는 옵션이 있습니다 next/router 의 <Link> 컴포넌트의 요소에 scroll false 옵션을 전달할 수 있고 router.push()에는 scroll false props가 있습니다.

router.push('/url', undefined, { scroll: false });

 

하지만 위의 옵션들은 스크롤이 window를 참조하는 경우에 작동하지만 특이 케이스에 따라 div 구성 요소를 참조하는 경우에는 작동하지 않을수 있습니다.

이 경우 ref를 임의로 만든 커스텀 스크롤 훅으로 전달해야 합니다.



저같은 경우의 페이지 스크롤은 div를 참조하고 있었고 이 div의 높이는 페이지의 불러오는 사이즈에 따라서 div의 높이가 달라지는 경우였습니다.

처음 렌더링할 때 아이템이 비어 있으므로 그 당시 스크롤 높이는 `window.clientHeight`까지만 확장할 수 있었습니다.


해결방안

저는 react-query를 사용하고 있었고 staleTime 옵션에서 browser의 In-memory에 데이터를 유지하기 위해 useInfiniteQuery를 사용했습니다.

이렇게하면 페이지를 이동하고 다시 페이지로 돌아가더라도 데이터가 캐시되고 항목을 계속 유지하고 스크롤 높이가 이전 높이까지 확장할 수 있엇습니다.

이전 스크롤 높이로 유지하려면 div를 참조하는 ref의 `scrollTop`을  session storage에 저장하고 session storage의 scroll height을 UseEffect() 로직안에 불러온 다음 불러오는 아이템의 변수를 dependency에 추가하였습니다.

이 방법으로 스크롤을 복원할 수 있습니다.

export const SCROLL_HEIGHT_KEY = "SCROLL_HEIGHT";

export const Pagination = () => {
const [scrollViewRef, setScrollViewRef] = useState<HTMLDivElement | null>();
const {
    data: allDatas,
    fetchNextPage,
  } = useInfiniteLibrary(someFilter);

...

const onScroll = useCallback(
    (event: any) => {
      let element = event.target;
      window.sessionStorage.setItem(SCROLL_HEIGHT_KEY, String(element.scrollTop));

      if (someConditions) {
        fetchNextPage();
      }
    },
    [debouncedSearch, fetchNextPage, myLibrary],
  );

...

useEffect(() => {
    const scrollHeight = window.sessionStorage.getItem(SCROLL_HEIGHT_KEY);
    if (scrollViewRef && scrollHeight) {
      scrollViewRef.scrollTo({
        top: parseInt(scrollHeight, 10),
        behavior: "auto",
      });
    }
  }, [allDatas, scrollViewRef]);

...

return (
<SrollDiv onScroll={onScroll} ref={(ref) => setScrollViewRef(ref)}>
...
</ScrollDiv>
)
}