Skip to content

Commit 23344c0

Browse files
committed
Finish migration and keep previous compatibility
1 parent 8373529 commit 23344c0

File tree

7 files changed

+142
-30
lines changed

7 files changed

+142
-30
lines changed

packages/gatsby-react-router-scroll/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@
3333
"directory": "packages/gatsby-react-router-scroll"
3434
},
3535
"scripts": {
36-
"build": "babel src --out-dir . --ignore \"**/__tests__\"",
36+
"build": "babel src --out-dir . --ignore \"**/__tests__\" --extensions \".ts,.tsx\"",
3737
"prepare": "cross-env NODE_ENV=production npm run build",
38-
"watch": "babel -w src --out-dir . --ignore \"**/__tests__\""
38+
"watch": "babel -w src --out-dir . --ignore \"**/__tests__\" --extensions \".ts,.tsx\""
3939
},
4040
"engines": {
4141
"node": ">=10.13.0"

packages/gatsby-react-router-scroll/src/index.js

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ScrollHandler as ScrollContext } from "./scroll-handler"
2+
import { ScrollContainer } from "./scroll-container"
3+
import { useScrollRestoration } from "./use-scroll-restoration"
4+
5+
export { ScrollContext, ScrollContainer, useScrollRestoration }
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from "react"
2+
import ReactDOM from "react-dom"
3+
import PropTypes from "prop-types"
4+
import { ScrollContext } from "./scroll-handler"
5+
import { SessionStorage } from "./session-storage"
6+
import { Location } from "@reach/router"
7+
import { Location as HLocation } from "history"
8+
9+
const propTypes = {
10+
scrollKey: PropTypes.string.isRequired,
11+
shouldUpdateScroll: PropTypes.func,
12+
children: PropTypes.element.isRequired,
13+
}
14+
15+
type Props = {
16+
scrollKey: string
17+
shouldUpdateScroll?: Function
18+
children: React.ReactNode
19+
}
20+
21+
type PropsWithContextAndLocation = Props & {
22+
context: SessionStorage
23+
location: HLocation
24+
}
25+
26+
class ScrollContainerImplementation extends React.Component<
27+
PropsWithContextAndLocation
28+
> {
29+
componentDidMount() {
30+
const node = ReactDOM.findDOMNode(this) as Element
31+
const { location, scrollKey } = this.props
32+
33+
if (!node) return
34+
35+
node.addEventListener("scroll", () => {
36+
this.props.context.save(location, scrollKey, node.scrollTop)
37+
})
38+
39+
const position = this.props.context.read(location, scrollKey)
40+
node.scrollTo(0, position)
41+
}
42+
43+
render() {
44+
return this.props.children
45+
}
46+
}
47+
48+
export const ScrollContainer = (props: Props) => (
49+
<Location>
50+
{({ location }) => (
51+
<ScrollContext.Consumer>
52+
{context => (
53+
<ScrollContainerImplementation
54+
{...props}
55+
context={context}
56+
location={location}
57+
/>
58+
)}
59+
</ScrollContext.Consumer>
60+
)}
61+
</Location>
62+
)
63+
64+
ScrollContainer.propTypes = propTypes

packages/gatsby-react-router-scroll/src/ScrollBehaviorContext.js renamed to packages/gatsby-react-router-scroll/src/scroll-handler.tsx

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
11
import React from "react"
2+
import { LocationContext } from "@reach/router"
23
import PropTypes from "prop-types"
3-
import SessionStorage from "./StateStorage"
4-
5-
export default class ScrollContext extends React.Component {
6-
constructor(props, context) {
7-
super(props, context)
8-
9-
this._stateStorage = new SessionStorage()
4+
import { SessionStorage } from "./session-storage"
5+
6+
export const ScrollContext = React.createContext<SessionStorage>(
7+
new SessionStorage()
8+
)
9+
ScrollContext.displayName = `GatsbyScrollContext`
10+
11+
type ShouldUpdateScrollFn = (
12+
prevRouterProps: LocationContext | undefined,
13+
routerProps: LocationContext
14+
) => boolean
15+
type ShouldUpdateScroll = undefined | ShouldUpdateScrollFn
16+
17+
export class ScrollHandler extends React.Component<
18+
LocationContext & { shouldUpdateScroll: ShouldUpdateScroll }
19+
> {
20+
static propTypes = {
21+
shouldUpdateScroll: PropTypes.func,
22+
children: PropTypes.element.isRequired,
23+
location: PropTypes.object.isRequired,
1024
}
1125

26+
_stateStorage: SessionStorage = new SessionStorage()
27+
1228
scrollListener = () => {
1329
const { key } = this.props.location
1430

@@ -23,17 +39,17 @@ export default class ScrollContext extends React.Component {
2339
this.props.location.key
2440
)
2541
if (scrollPosition) {
26-
this.windowScroll(scrollPosition)
42+
this.windowScroll(scrollPosition, undefined)
2743
} else if (this.props.location.hash) {
28-
this.scrollToHash(decodeURI(this.props.location.hash))
44+
this.scrollToHash(decodeURI(this.props.location.hash), undefined)
2945
}
3046
}
3147

3248
componentWillUnmount() {
3349
window.removeEventListener(`scroll`, this.scrollListener)
3450
}
3551

36-
componentDidUpdate(prevProps) {
52+
componentDidUpdate(prevProps: LocationContext): void {
3753
const { hash } = this.props.location
3854

3955
const scrollPosition = this._stateStorage.read(
@@ -47,21 +63,30 @@ export default class ScrollContext extends React.Component {
4763
}
4864
}
4965

50-
windowScroll = (position, prevProps) => {
66+
windowScroll = (
67+
position: number,
68+
prevProps: LocationContext | undefined
69+
): void => {
5170
if (this.shouldUpdateScroll(prevProps, this.props)) {
5271
window.scroll(0, position)
5372
}
5473
}
5574

56-
scrollToHash = (hash, prevProps) => {
75+
scrollToHash = (
76+
hash: string,
77+
prevProps: LocationContext | undefined
78+
): void => {
5779
const node = document.querySelector(hash)
5880

5981
if (node && this.shouldUpdateScroll(prevProps, this.props)) {
6082
node.scrollIntoView()
6183
}
6284
}
6385

64-
shouldUpdateScroll = (prevRouterProps, routerProps) => {
86+
shouldUpdateScroll = (
87+
prevRouterProps: LocationContext | undefined,
88+
routerProps: LocationContext
89+
): boolean => {
6590
const { shouldUpdateScroll } = this.props
6691
if (!shouldUpdateScroll) {
6792
return true
@@ -72,12 +97,10 @@ export default class ScrollContext extends React.Component {
7297
}
7398

7499
render() {
75-
return this.props.children
100+
return (
101+
<ScrollContext.Provider value={this._stateStorage}>
102+
{this.props.children}
103+
</ScrollContext.Provider>
104+
)
76105
}
77106
}
78-
79-
ScrollContext.propTypes = {
80-
shouldUpdateScroll: PropTypes.func,
81-
children: PropTypes.element.isRequired,
82-
location: PropTypes.object.isRequired,
83-
}

packages/gatsby-react-router-scroll/src/StateStorage.js renamed to packages/gatsby-react-router-scroll/src/session-storage.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
import { Location } from "history"
12
const STATE_KEY_PREFIX = `@@scroll|`
23
const GATSBY_ROUTER_SCROLL_STATE = `___GATSBY_REACT_ROUTER_SCROLL`
34

4-
export default class SessionStorage {
5-
read(location, key) {
5+
export class SessionStorage {
6+
read(location: Location, key: string): number {
67
const stateKey = this.getStateKey(location, key)
78

89
try {
910
const value = window.sessionStorage.getItem(stateKey)
10-
return JSON.parse(value)
11+
return value ? JSON.parse(value) : 0
1112
} catch (e) {
1213
if (process.env.NODE_ENV !== `production`) {
1314
console.warn(
@@ -23,11 +24,11 @@ export default class SessionStorage {
2324
return window[GATSBY_ROUTER_SCROLL_STATE][stateKey]
2425
}
2526

26-
return {}
27+
return 0
2728
}
2829
}
2930

30-
save(location, key, value) {
31+
save(location: Location, key: string, value: number) {
3132
const stateKey = this.getStateKey(location, key)
3233
const storedValue = JSON.stringify(value)
3334

@@ -49,7 +50,7 @@ export default class SessionStorage {
4950
}
5051
}
5152

52-
getStateKey(location, key) {
53+
getStateKey(location: Location, key: string) {
5354
const locationKey = location.key || location.pathname
5455
const stateKeyBase = `${STATE_KEY_PREFIX}${locationKey}`
5556
return key === null || typeof key === `undefined`
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ScrollContext } from "./scroll-handler"
2+
import { useRef, useContext, useLayoutEffect } from "react"
3+
import { useLocation } from "@reach/router"
4+
5+
export function useScrollRestoration(identifier: string) {
6+
const location = useLocation()
7+
const state = useContext(ScrollContext)
8+
const ref = useRef<HTMLElement>()
9+
10+
useLayoutEffect(() => {
11+
const position = state.read(location, identifier)
12+
ref.current!.scrollTo(0, position)
13+
}, [])
14+
15+
return {
16+
ref,
17+
onScroll() {
18+
state.save(location, identifier, ref.current!.scrollTop)
19+
},
20+
}
21+
}

0 commit comments

Comments
 (0)