From 83735299e4b998a103b1629e436ed8af89cf1066 Mon Sep 17 00:00:00 2001 From: Blaine Kasten Date: Wed, 20 May 2020 14:08:55 -0500 Subject: [PATCH] fix(gatsby-react-router-scroll): Major improvements to scroll handling --- .../gatsby-react-router-scroll/package.json | 4 +- .../src/ScrollBehaviorContext.js | 110 +++++++++--------- .../src/ScrollContainer.js | 83 ------------- .../gatsby-react-router-scroll/src/index.js | 2 - yarn.lock | 55 +++++---- 5 files changed, 84 insertions(+), 170 deletions(-) delete mode 100644 packages/gatsby-react-router-scroll/src/ScrollContainer.js diff --git a/packages/gatsby-react-router-scroll/package.json b/packages/gatsby-react-router-scroll/package.json index 6a3f1ca9edf65..066bcc16503c7 100644 --- a/packages/gatsby-react-router-scroll/package.json +++ b/packages/gatsby-react-router-scroll/package.json @@ -7,9 +7,7 @@ "url": "https://github.com/gatsbyjs/gatsby/issues" }, "dependencies": { - "@babel/runtime": "^7.9.6", - "scroll-behavior": "^0.9.12", - "warning": "^3.0.0" + "@babel/runtime": "^7.9.6" }, "devDependencies": { "@babel/cli": "^7.8.4", diff --git a/packages/gatsby-react-router-scroll/src/ScrollBehaviorContext.js b/packages/gatsby-react-router-scroll/src/ScrollBehaviorContext.js index 8cb69e53fbd0b..a8f9ae0807ede 100644 --- a/packages/gatsby-react-router-scroll/src/ScrollBehaviorContext.js +++ b/packages/gatsby-react-router-scroll/src/ScrollBehaviorContext.js @@ -1,51 +1,64 @@ import React from "react" -import ScrollBehavior from "scroll-behavior" import PropTypes from "prop-types" -import { globalHistory as history } from "@reach/router/lib/history" import SessionStorage from "./StateStorage" -export const ScrollBehaviorContext = React.createContext() - -const propTypes = { - shouldUpdateScroll: PropTypes.func, - children: PropTypes.element.isRequired, - location: PropTypes.object.isRequired, -} - -class ScrollContext extends React.Component { +export default class ScrollContext extends React.Component { constructor(props, context) { super(props, context) - this.scrollBehavior = new ScrollBehavior({ - addTransitionHook: history.listen, - stateStorage: new SessionStorage(), - getCurrentLocation: () => this.props.location, - shouldUpdateScroll: this.shouldUpdateScroll, - }) + this._stateStorage = new SessionStorage() } - componentDidUpdate(prevProps) { - const { location } = this.props - const prevLocation = prevProps.location + scrollListener = () => { + const { key } = this.props.location - if (location === prevLocation) { - return - } + this._stateStorage.save(this.props.location, key, window.scrollY) + } - const prevRouterProps = { - location: prevProps.location, - } + componentDidMount() { + window.addEventListener(`scroll`, this.scrollListener) - this.scrollBehavior.updateScroll(prevRouterProps, { history, location }) + const scrollPosition = this._stateStorage.read( + this.props.location, + this.props.location.key + ) + if (scrollPosition) { + this.windowScroll(scrollPosition) + } else if (this.props.location.hash) { + this.scrollToHash(decodeURI(this.props.location.hash)) + } } componentWillUnmount() { - this.scrollBehavior.stop() + window.removeEventListener(`scroll`, this.scrollListener) } - getRouterProps() { - const { location } = this.props - return { location, history } + componentDidUpdate(prevProps) { + const { hash } = this.props.location + + const scrollPosition = this._stateStorage.read( + this.props.location, + this.props.location.key + ) + if (scrollPosition) { + this.windowScroll(scrollPosition, prevProps) + } else if (hash) { + this.scrollToHash(decodeURI(hash), prevProps) + } + } + + windowScroll = (position, prevProps) => { + if (this.shouldUpdateScroll(prevProps, this.props)) { + window.scroll(0, position) + } + } + + scrollToHash = (hash, prevProps) => { + const node = document.querySelector(hash) + + if (node && this.shouldUpdateScroll(prevProps, this.props)) { + node.scrollIntoView() + } } shouldUpdateScroll = (prevRouterProps, routerProps) => { @@ -54,36 +67,17 @@ class ScrollContext extends React.Component { return true } - // Hack to allow accessing scrollBehavior._stateStorage. - return shouldUpdateScroll.call( - this.scrollBehavior, - prevRouterProps, - routerProps - ) - } - - registerElement = (key, element, shouldUpdateScroll) => { - this.scrollBehavior.registerElement( - key, - element, - shouldUpdateScroll, - this.getRouterProps() - ) - } - - unregisterElement = key => { - this.scrollBehavior.unregisterElement(key) + // Hack to allow accessing this._stateStorage. + return shouldUpdateScroll.call(this, prevRouterProps, routerProps) } render() { - return ( - - {React.Children.only(this.props.children)} - - ) + return this.props.children } } -ScrollContext.propTypes = propTypes - -export default ScrollContext +ScrollContext.propTypes = { + shouldUpdateScroll: PropTypes.func, + children: PropTypes.element.isRequired, + location: PropTypes.object.isRequired, +} diff --git a/packages/gatsby-react-router-scroll/src/ScrollContainer.js b/packages/gatsby-react-router-scroll/src/ScrollContainer.js deleted file mode 100644 index 1373b8c41791c..0000000000000 --- a/packages/gatsby-react-router-scroll/src/ScrollContainer.js +++ /dev/null @@ -1,83 +0,0 @@ -import React from "react" -import ReactDOM from "react-dom" -import warning from "warning" -import PropTypes from "prop-types" -import { ScrollBehaviorContext } from "./ScrollBehaviorContext" - -const propTypes = { - scrollKey: PropTypes.string.isRequired, - shouldUpdateScroll: PropTypes.func, - children: PropTypes.element.isRequired, -} - -class ScrollContainer extends React.Component { - constructor(props) { - super(props) - - // We don't re-register if the scroll key changes, so make sure we - // unregister with the initial scroll key just in case the user changes it. - this.scrollKey = props.scrollKey - } - - componentDidMount() { - this.props.context.registerElement( - this.props.scrollKey, - ReactDOM.findDOMNode(this), // eslint-disable-line react/no-find-dom-node - this.shouldUpdateScroll - ) - - // Only keep around the current DOM node in development, as this is only - // for emitting the appropriate warning. - if (process.env.NODE_ENV !== `production`) { - this.domNode = ReactDOM.findDOMNode(this) // eslint-disable-line react/no-find-dom-node - } - } - - componentDidUpdate(prevProps) { - warning( - prevProps.scrollKey === this.props.scrollKey, - ` does not support changing scrollKey.` - ) - if (process.env.NODE_ENV !== `production`) { - const prevDomNode = this.domNode - this.domNode = ReactDOM.findDOMNode(this) // eslint-disable-line react/no-find-dom-node - - warning( - this.domNode === prevDomNode, - ` does not support changing DOM node.` - ) - } - } - - componentWillUnmount() { - this.props.context.unregisterElement(this.scrollKey) - } - - shouldUpdateScroll = (prevRouterProps, routerProps) => { - const { shouldUpdateScroll } = this.props - if (!shouldUpdateScroll) { - return true - } - - // Hack to allow accessing scrollBehavior._stateStorage. - return shouldUpdateScroll.call( - this.props.context.scrollBehavior, - prevRouterProps, - routerProps - ) - } - - render() { - return this.props.children - } -} - -const ScrollContainerConsumer = props => ( - - {context => } - -) - -ScrollContainerConsumer.propTypes = propTypes - -export default ScrollContainerConsumer diff --git a/packages/gatsby-react-router-scroll/src/index.js b/packages/gatsby-react-router-scroll/src/index.js index d0f30139c3da6..66d9077fb7495 100644 --- a/packages/gatsby-react-router-scroll/src/index.js +++ b/packages/gatsby-react-router-scroll/src/index.js @@ -1,4 +1,2 @@ import ScrollBehaviorContext from "./ScrollBehaviorContext" -import ScrollContainer from "./ScrollContainer" -exports.ScrollContainer = ScrollContainer exports.ScrollContext = ScrollBehaviorContext diff --git a/yarn.lock b/yarn.lock index e962dcffea62d..3c19d7566c281 100644 --- a/yarn.lock +++ b/yarn.lock @@ -116,7 +116,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.6.6", "@babel/core@^7.8.7", "@babel/core@^7.9.6": +"@babel/core@^7.8.7", "@babel/core@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376" integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg== @@ -2015,13 +2015,6 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.8.4": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" - integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/runtime@^7.6.3": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.4.tgz#b23a856751e4bf099262f867767889c0e3fe175b" @@ -2036,20 +2029,27 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" - integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== +"@babel/runtime@^7.7.4", "@babel/runtime@^7.8.4": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" + integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.9.6": +"@babel/runtime@^7.8.7", "@babel/runtime@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.9.2": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" + integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/standalone@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.9.6.tgz#7a5f82c6fa29959b12f708213be6de8ec0b79338" @@ -8551,7 +8551,7 @@ cssstyle@^2.2.0: dependencies: cssom "~0.3.6" -csstype@^2.2.0: +csstype@^2.2.0, csstype@^2.6.7: version "2.6.10" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b" integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w== @@ -9331,12 +9331,13 @@ dom-converter@~0.1: dependencies: utila "~0.3" -dom-helpers@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" - integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== +dom-helpers@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b" + integrity sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A== dependencies: - "@babel/runtime" "^7.1.2" + "@babel/runtime" "^7.8.7" + csstype "^2.6.7" dom-serializer@0, dom-serializer@~0.1.0, dom-serializer@~0.1.1: version "0.1.1" @@ -18369,6 +18370,11 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" +page-lifecycle@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/page-lifecycle/-/page-lifecycle-0.1.2.tgz#f17a083c082bd5ababddd77f1025a4b1c8808012" + integrity sha512-+3uccYgL0CXG0KSXRxZi4uc2E6mqFWV5HqiJJgcnaJCiS0LqiuJ4vB420N21NFuLvuvLB4Jr5drgQ2NXAXF9Iw== + pako@^0.2.5: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" @@ -22270,13 +22276,14 @@ schemes@^1.0.1: dependencies: extend "^3.0.0" -scroll-behavior@^0.9.12: - version "0.9.12" - resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.9.12.tgz#1c22d273ec4ce6cd4714a443fead50227da9424c" - integrity sha512-18sirtyq1P/VsBX6O/vgw20Np+ngduFXEMO4/NDFXabdOKBL2kjPVUpz1y0+jm99EWwFJafxf5/tCyMeXt9Xyg== +scroll-behavior@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.11.0.tgz#fff2765b6007341b80a04678fcd314e54d5b03ea" + integrity sha512-wQvNs3Q1TRvEkkwrFd/BkIL+dA4PYQl55/FUlmtjgz63/FtbnyR6MkLyRmjK0Rg3LCZCr0jORsFfMLkeNYdFuA== dependencies: - dom-helpers "^3.4.0" + dom-helpers "^5.1.4" invariant "^2.2.4" + page-lifecycle "^0.1.2" section-matter@^1.0.0: version "1.0.0"