Last active
August 2, 2024 06:25
-
-
Save GusRuss89/df05ea25310043fc38a5e2ba3cb0c016 to your computer and use it in GitHub Desktop.
Nextjs - keep state and scroll position between page transitions. Described in detail here - https://medium.com/@angus.russell89/next-js-keep-page-components-mounted-between-page-transitions-and-maintain-scroll-position-205b34539a26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useRef, useEffect, memo } from 'react' | |
import { useRouter } from 'next/router' | |
const ROUTES_TO_RETAIN = ['/dashboard', '/top', '/recent', 'my-posts'] | |
const App = ({ Component, pageProps }) => { | |
const router = useRouter() | |
const retainedComponents = useRef({}) | |
const isRetainableRoute = ROUTES_TO_RETAIN.includes(router.asPath) | |
// Add Component to retainedComponents if we haven't got it already | |
if (isRetainableRoute && !retainedComponents.current[router.asPath]) { | |
const MemoComponent = memo(Component) | |
retainedComponents.current[router.asPath] = { | |
component: <MemoComponent {...pageProps} />, | |
scrollPos: 0 | |
} | |
} | |
// Save the scroll position of current page before leaving | |
const handleRouteChangeStart = url => { | |
if (isRetainableRoute) { | |
retainedComponents.current[router.asPath].scrollPos = window.scrollY | |
} | |
} | |
// Save scroll position - requires an up-to-date router.asPath | |
useEffect(() => { | |
router.events.on('routeChangeStart', handleRouteChangeStart) | |
return () => { | |
router.events.off('routeChangeStart', handleRouteChangeStart) | |
} | |
}, [router.asPath]) | |
// Scroll to the saved position when we load a retained component | |
useEffect(() => { | |
if (isRetainableRoute) { | |
window.scrollTo(0, retainedComponents.current[router.asPath].scrollPos) | |
} | |
}, [Component, pageProps]) | |
return ( | |
<div> | |
<div style={{ display: isRetainableRoute ? 'block' : 'none' }}> | |
{Object.entries(retainedComponents.current).map(([path, c]) => ( | |
<div | |
key={path} | |
style={{ display: router.asPath === path ? 'block' : 'none' }} | |
> | |
{c.component} | |
</div> | |
))} | |
</div> | |
{!isRetainableRoute && <Component {...pageProps} />} | |
</div> | |
) | |
} | |
export default App |
@nandorojo I'm not familiar with ScrollView
but surely it has some way to get and set the scroll position? If so it should be exactly the same but with those methods instead of window.scrollY
and window.scrollTo()
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Any thoughts on how to achieve this without window scrolling? For instance, if I’m using a
ScrollView
from React Native Web (which is just a div withoverflow-y: scroll
) and I want to restore its scroll position. I suppose I could try to track it globally somehow, but it’s a bit cumbersome to solve.