It's not so easy to restore scrollY position while navigating. We might need a context for storing y positions for pages. Below is my solution.
custom hook for listening scroll event(hooks/scroll.js)
import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
export const useScrollPositions = () => {
const [positions, setPositions] = useState(new Map());
const location = useLocation();
useEffect(() => {
const listener = (ev) => {
console.log('position: ', ev.target.scrollTop);
positions.set(location.pathname, {
left: ev.target.scrollLeft,
top: ev.target.scrollTop
});
setPositions(new Map(positions));
}
window.addEventListener('scroll', listener, true);
return () => {
window.removeEventListener('scroll', listener, true);
}
}, [location]);
return positions;
}
context for storing y positions for pages(context/scroll-context.js)
import { createContext } from 'react';
export const ScrollContext = createContext();
Wrapping all pages inside context provider: App component
const App = () => {
const location = useLocation();
const positions = useScrollPositions();
const transitions = useTransition(location, {
from: { opacity: 0, transform: 'translate3d(100vw, 0, 0)' },
enter: { opacity: 1, transform: 'translate3d(0, 0, 0)' },
leave: { opacity: 0, transform: 'translate3d(-20vw, 0, 0)' },
});
return (
<ScrollContext.Provider value={positions}>
<GlobalStyles />
<Switch>
<Route path='/' exact component={Home} />
<Route path="/about" component={About} />
</Switch>
{transitions((props, item) => (
<animated.div style={props}>
<Switch location={item}>
<Route path="/" exact component={Home} />
<Route path="/about" exact component={About} />
</Switch>
</animated.div>
))}
</ScrollContext.Provider>
);
};
Page component that does scrolling
import React, { useEffect, useRef, useContext } from 'react';
import { useLocation } from 'react-router-dom';
import { ScrollContext } from '../context/scroll-context';
const Page = ({children, ...rest}) => {
const main = useRef();
const positions = useContext(ScrollContext);
const location = useLocation();
useEffect(() => {
if (main.current) {
const position = positions.get(location.pathname);
if (position && position.top) {
main.current.scrollTop = position.top;
}
}
}, [])
return (
<main {...rest} ref={main}>
{children}
</main>
)
}
export default Page
Note: You have to wrap all pages inside this page component. CSS style of this page component(scroll: auto) is also important. In real world projects, it might needs more flexible codes. Anyway, for a full working project, please check here