Skip to content

Commit

Permalink
Scroll Restoration between Views
Browse files Browse the repository at this point in the history
  • Loading branch information
martpie committed Sep 19, 2024
1 parent 15a967a commit 6cdb95b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/components/TracksList/TracksList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { usePlayerAPI } from '../../stores/usePlayerStore';
import TrackRow from '../TrackRow/TrackRow';
import TracksListHeader from '../TracksListHeader/TracksListHeader';

import { useScrollRestoration } from '../../hooks/useScrollRestoration';
import styles from './TracksList.module.css';

const ROW_HEIGHT = 30;
Expand Down Expand Up @@ -87,6 +88,7 @@ export default function TracksList(props: Props) {
const playerAPI = usePlayerAPI();
const libraryAPI = useLibraryAPI();
const highlight = useLibraryStore((state) => state.highlightPlayingTrack);
useScrollRestoration(scrollableRef);

// Highlight playing track and scroll to it
// Super-mega-hacky to use Redux for that
Expand Down
43 changes: 43 additions & 0 deletions src/hooks/useScrollRestoration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect } from 'react';
import { useLocation, useNavigation } from 'react-router-dom';

function getScrollPosition(key: string) {
const pos = window.sessionStorage.getItem(key);
return Number(pos) || 0;
}

function setScrollPosition(key: string, pos: number) {
window.sessionStorage.setItem(key, pos.toString());
}

/**
* Given a ref to a scrolling container element, keep track of its scroll
* position before navigation and restore it on return (e.g., back/forward nav).
*
* Inspired by:
* https://github.com/remix-run/react-router/pull/10468#issuecomment-1877312374
*
* Except we want to use location.pathname, and not location.key
*/
export function useScrollRestoration(container: React.RefObject<HTMLElement>) {
const key = `scroll-position-${useLocation().pathname}`;
const { state } = useNavigation();

useEffect(() => {
function onScroll() {
setScrollPosition(key, container.current?.scrollTop ?? 0);
}

container.current?.addEventListener('scroll', onScroll);

return () => {
container.current?.removeEventListener('scroll', onScroll);
};
});

useEffect(() => {
if (state === 'idle') {
container.current?.scrollTo(0, getScrollPosition(key));
}
}, [key, state, container]);
}

0 comments on commit 6cdb95b

Please sign in to comment.