Scroll restoration in Nextjs
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>
)
}