Skip to content

Instantly share code, notes, and snippets.

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

This comment has been minimized.

Copy link

@asktree 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

This comment has been minimized.

Copy link

@okvv okvv commented Dec 23, 2019

Why not refactor componentDidUpdate block to:

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

@aroraenterprise

This comment has been minimized.

Copy link

@aroraenterprise 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