Uses a feature of React Router v6 internally to have more flexibility over naming of the keys.
Needs two params:
- A key to uniquely identify the list in the page
- A reference to list's HTML element
Inspired by a hook used in Remix. (Watch this video)
Uses a feature of React Router v6 internally to have more flexibility over naming of the keys.
Needs two params:
Inspired by a hook used in Remix. (Watch this video)
| import { RefObject, useLayoutEffect } from 'react'; | |
| import { useLocation } from 'react-router-dom'; | |
| const scrollMap: Record<string, Record<string, number>> = {}; | |
| const useScrollRestoration = ( | |
| key: string, | |
| ref: RefObject<HTMLElement | null> | |
| ) => { | |
| const { pathname } = useLocation(); | |
| // set key if not present | |
| useLayoutEffect(() => { | |
| const isPathPresent = Object.keys(scrollMap).includes(pathname); | |
| if (!isPathPresent) { | |
| scrollMap[pathname] = {}; | |
| } | |
| const isKeyPresent = Object.keys(scrollMap[pathname]).includes(key); | |
| const el = ref.current; | |
| if (el == null) return; | |
| if (!isKeyPresent) { | |
| scrollMap[pathname][key] = el.scrollTop; | |
| } | |
| }, [key, pathname, ref]); | |
| // restore scroll if its data is present | |
| useLayoutEffect(() => { | |
| const el = ref.current; | |
| if (el == null) return; | |
| el.scrollTop = scrollMap[pathname][key]; | |
| }, [key, pathname, ref]); | |
| // retrieve and store scroll info before it unmounts | |
| useLayoutEffect(() => { | |
| const el = ref.current; | |
| if (el == null) return; | |
| return () => { | |
| scrollMap[pathname][key] = el.scrollTop; | |
| }; | |
| }, [key, pathname, ref]); | |
| }; | |
| export default useScrollRestoration; |