Skip to content

Instantly share code, notes, and snippets.

@ajitid
Last active March 30, 2022 20:41
Show Gist options
  • Save ajitid/c33369d3cd9c2667f78ef5b8b996128e to your computer and use it in GitHub Desktop.
Save ajitid/c33369d3cd9c2667f78ef5b8b996128e to your computer and use it in GitHub Desktop.
Restores scroll in React app

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)

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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment