younes khanbaba
younes khanbaba

Reputation: 328

Nextjs how to not unmount previous page when going to next page (to keep state)

we are using Nextjs in our web app. We want to keep stack of pages where users visit to keep state of component on back navigation. How should we do that? I have tried https://github.com/exogen/next-modal-pages, but it calls getInitialProps of previous pages again on back.

Upvotes: 3

Views: 9217

Answers (2)

GusRuss89
GusRuss89

Reputation: 1404

Here's my solution with a custom _app.js

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

Gist - https://gist.github.com/GusRuss89/df05ea25310043fc38a5e2ba3cb0c016

Upvotes: 7

felixmosh
felixmosh

Reputation: 35553

You can't "save the state of the page by not un-mounting it" but you can save the state of your app in _app.js file, and the rebuild the previous page from it.

Check the redux example from next's repo.

Upvotes: 0

Related Questions