@@ -3,33 +3,58 @@ import { useEffect } from 'react';
33import { useRouter } from 'next/router' ;
44
55const scrollPositions : Record < string , number > = { } ;
6+ const RESTORE_TIMEOUT_MS = 1000 ;
7+
8+ const getScrollKey = ( asPath : string ) : string => {
9+ if ( typeof window === 'undefined' ) {
10+ return asPath ;
11+ }
12+ const historyKey = ( window . history . state as { key ?: string } | null ) ?. key ;
13+ return historyKey ? `${ asPath } :${ historyKey } ` : asPath ;
14+ } ;
615
716export const useScrollRestoration = ( ) : void => {
8- const { pathname } = useRouter ( ) ;
17+ const { asPath } = useRouter ( ) ;
918
1019 useEffect ( ( ) => {
1120 const handleScroll = ( ) => {
12- scrollPositions [ pathname ] = window . scrollY ;
21+ scrollPositions [ getScrollKey ( asPath ) ] = window . scrollY ;
1322 } ;
1423
15- window . addEventListener ( 'scroll' , handleScroll ) ;
24+ window . addEventListener ( 'scroll' , handleScroll , { passive : true } ) ;
1625
1726 return ( ) => {
1827 window . removeEventListener ( 'scroll' , handleScroll ) ;
1928 } ;
20- } , [ pathname ] ) ;
29+ } , [ asPath ] ) ;
2130
2231 useEffect ( ( ) => {
23- const scrollPosition = scrollPositions [ pathname ] || 0 ;
32+ const target = scrollPositions [ getScrollKey ( asPath ) ] ?? 0 ;
33+ if ( target === 0 ) {
34+ return undefined ;
35+ }
36+
37+ // Wait until the page is tall enough before scrolling, so we don't clamp
38+ // to the bottom while feed content is still hydrating.
39+ const deadline = performance . now ( ) + RESTORE_TIMEOUT_MS ;
40+ let frame = 0 ;
41+
42+ const tick = ( ) => {
43+ const maxScroll =
44+ document . documentElement . scrollHeight - window . innerHeight ;
45+
46+ if ( maxScroll >= target || performance . now ( ) >= deadline ) {
47+ window . scrollTo ( 0 , Math . min ( target , Math . max ( 0 , maxScroll ) ) ) ;
48+ return ;
49+ }
50+
51+ frame = requestAnimationFrame ( tick ) ;
52+ } ;
2453
25- // Add a small delay to ensure content is loaded before restoring scroll
26- // This is especially important for feed pages that load content dynamically
27- const timeoutId = setTimeout ( ( ) => {
28- window . scrollTo ( 0 , scrollPosition ) ;
29- } , 50 ) ;
54+ frame = requestAnimationFrame ( tick ) ;
3055
31- return ( ) => clearTimeout ( timeoutId ) ;
32- } , [ pathname ] ) ;
56+ return ( ) => cancelAnimationFrame ( frame ) ;
57+ } , [ asPath ] ) ;
3358} ;
3459
3560export const useManualScrollRestoration = ( ) : void => {
0 commit comments