Created
November 13, 2021 12:24
-
-
Save firejune/6b570fb8ac5d98cf9cef5f435e527b05 to your computer and use it in GitHub Desktop.
Scroll position save and restoration for the given url
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useRef, useEffect } from 'react' | |
import { useRouter } from 'next/router' | |
// Save the scroll position for the given url | |
function saveScrollPosition(url: string, element: HTMLElement, savePosition: (url: string, pos: number) => void) { | |
savePosition(url, element.scrollTop) | |
} | |
// Restore the scroll position for the given url is possible | |
function restoreScrollPosition( | |
url: string, | |
element: HTMLElement, | |
positions: React.RefObject<{ [key: string]: number }> | |
) { | |
const position = positions.current[url] | |
if (position) { | |
// console.log('[SCROLL_POSITION]', 'restore', url, position) | |
element.scrollTo({ top: position }) | |
} | |
} | |
export default function useKeepScrollPosition() { | |
const router = useRouter() | |
const asPath = useRef(router.asPath) | |
const positions = useRef<{ [key: string]: number }>({}) | |
const updatePosition = (url: string, position: number) => { | |
// console.log('[SCROLL_POSITION]', 'update', url, position) | |
positions.current = { ...positions.current, [url]: position } | |
} | |
useEffect(() => { | |
if ('scrollRestoration' in window.history) { | |
const element = document.documentElement | |
let shouldScrollRestore = false | |
window.history.scrollRestoration = 'manual' | |
const handleBeforeUnload = (event: BeforeUnloadEvent) => { | |
saveScrollPosition(router.asPath, element, updatePosition) | |
delete event.returnValue | |
} | |
const handleRouteChangeStart = (url: string) => { | |
if (url !== asPath.current) { | |
saveScrollPosition(asPath.current, element, updatePosition) | |
} | |
} | |
const handleRouteChangeComplete = (url: string) => { | |
asPath.current = url | |
if (shouldScrollRestore) { | |
shouldScrollRestore = false | |
restoreScrollPosition(url, element, positions) | |
} | |
} | |
window.addEventListener('beforeunload', handleBeforeUnload) | |
router.events.on('routeChangeStart', handleRouteChangeStart) | |
router.events.on('routeChangeComplete', handleRouteChangeComplete) | |
router.beforePopState(() => { | |
shouldScrollRestore = true | |
return true | |
}) | |
return () => { | |
window.removeEventListener('beforeunload', handleBeforeUnload) | |
router.events.off('routeChangeStart', handleRouteChangeStart) | |
router.events.off('routeChangeComplete', handleRouteChangeComplete) | |
router.beforePopState(() => true) | |
} | |
} | |
}, []) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment