Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Last active September 5, 2020 16:46
  • Star 21 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
import React from "react";
import { Location } from "@reach/router";
let scrollPositions = {};
class ManageScrollImpl extends React.Component {
componentDidMount() {
try {
// session storage will throw for a few reasons
// - user settings
// - in-cognito/private browsing
// - who knows...
let storage = JSON.parse(sessionStorage.getItem("scrollPositions"));
if (storage) {
scrollPositions = JSON.parse(storage) || {};
let { key } = this.props.location;
if (scrollPositions[key]) {
window.scrollTo(0, scrollPositions[key]);
}
}
} catch (e) {}
window.addEventListener("scroll", this.listener);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.listener);
}
componentDidUpdate() {
const { key } = this.props.location;
if (!scrollPositions[key]) {
// never seen this location before
window.scrollTo(0, 0);
} else {
// seen it
window.scrollTo(0, scrollPositions[key]);
}
}
listener = () => {
scrollPositions[this.props.location.key] = window.scrollY;
try {
sessionStorage.setItem(
"scrollPositions",
JSON.stringify(scrollPositions)
);
} catch (e) {}
};
render() {
return null;
}
}
export default () => (
<Location>
{({ location }) => <ManageScrollImpl location={location} />}
</Location>
);
@asktree
Copy link

asktree commented Oct 28, 2019

Is there any performance loss from having the listener constantly write to sessionStorage? Shouldn't it be possible to only update storage when I'm changing routes?

@okvv
Copy link

okvv commented Dec 23, 2019

Why not refactor componentDidUpdate block to:

const vistedLocationPosition = scrollPositions[this.props.location.key]
window.scrollTo(0, vistedLocationPosition || 0)

@aroraenterprise
Copy link

aroraenterprise commented Apr 2, 2020

For anyone looking for a functional component version of managing scroll position. I just import this component below my router <Router>...routes</Router><ManageScroll />

import { Location, WindowLocation } from "@reach/router";
import React from "react";

let scrollPositions: { [key: string]: number } = {};

type Props = {
	location: WindowLocation;
}

const ManageScrollImpl = ({ location }: Props) => {

	React.useEffect(() => {
		if (location.href) {
			window.scrollTo(0, scrollPositions[location.href || 0]);
		}
		window.addEventListener('scroll', listener);
		return () => window.removeEventListener('scroll', listener);
	}, [location?.href])

	const listener = () => {
		if (location && location.href) {
			scrollPositions[location.href] = window.scrollY;
		}
	};

	return null;
}

export const ManageScroll = () => (
	<Location>
		{({ location }) => <ManageScrollImpl location={location} />}
	</Location>
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment