import { useEffect, useLayoutEffect, useRef } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
const positions = new Map()
const MAX_ENTRIES = 50
const MAX_FRAMES = 60
export const useScrollRestoration = (ready = true) => {
const { pathname, search } = useLocation()
const history = useHistory()
const key = pathname + search
const handled = useRef(null)
const latest = useRef(0)
useEffect(() => {
latest.current = window.scrollY
const track = () => {
latest.current = window.scrollY
}
window.addEventListener('scroll', track, { passive: true })
return () => window.removeEventListener('scroll', track)
}, [key])
useLayoutEffect(
() => () => {
positions.delete(key)
positions.set(key, latest.current)
if (positions.size > MAX_ENTRIES) {
positions.delete(positions.keys().next().value)
}
},
[key],
)
useLayoutEffect(() => {
if (!ready || handled.current === key) return
handled.current = key
const saved = positions.get(key)
const top = history.action === 'POP' && saved !== undefined ? saved : 0
window.scrollTo({ top })
if (!top || Math.round(window.scrollY) === top) return
let frame
let frames = 0
let landed = Math.round(window.scrollY)
const retry = () => {
const y = Math.round(window.scrollY)
if (y === top || y !== landed || ++frames > MAX_FRAMES) return
window.scrollTo({ top })
landed = Math.round(window.scrollY)
frame = requestAnimationFrame(retry)
}
frame = requestAnimationFrame(retry)
return () => cancelAnimationFrame(frame)
}, [ready, key, history.action])
}