havingaheadache
havingaheadache

Reputation: 63

How to useMemo to delay React Children from display on routeChange in Next.js for page transition?

I'm trying to apply page transition effect in Next.js. Because of the project limitation, I cannot use external library except from GSAP. I've achieved similar effect using useMemo to track the children, and update the display children when the outro page transition completes. Then initiate the reveal the page after the display children updates.

But the only thing is when the page unmount (during outro-animation), some components are being re-run. The page got re-run only in build mode (not in dev mode). Any ideas/ direction on why this is happening? Or other method on applying the page transition is helpful. Thank you.

Here's the code: _app.tsx

function MainApp({ Component, pageProps }: AppProps) {
  return (
    <CustomPageTransition>
      <Component {...pageProps} key={router.asPath} />
    </CustomPageTransition>
  )
}

CustomPageTransition.tsx

export const CustomPageTransition = function ({ children }: { children: ReactNode}) {
  const [displayChildren, setDisplayChildren] = useState(children)
  const memoizedChildren = useMemo(() => children, [children])
  const [readyToRenderNextPage, setReadyToRenderNextPage] = useState(false)

  // startCover gsap animation
  const startCover = useCallback(() => {
    const coverTl = gsap.timeline()
    if (el.current) {
      coverTl.to(el.current, {
        opacity: 1,
        ease: 'Power2.inOut',
        duration: 0.3,
        onComplete: () => {
          setTimeout(() => {
            setReadyToRenderNextPage(true)
          }, 300)
        },
      })
    }
  }, [el])

  // outro page transition after first load
  useEffect(() => {
    //  on exit page
    router.events.on('routeChangeStart', startCover)

    return () => {
      router.events.off('routeChangeStart', startCover)
    }
  }, [router.events, startCover])

  // intro page transition after first load
  useEffect(() => {
    if (isIntroPageTransitionAnimating) {  // true when page component mounted, this is updated via Context
      startReveal()
    }
  }, [isIntroPageTransitionAnimating, startReveal])

  useIsomorphicLayoutEffect(() => {
    // outro: switch to new children when readyToRenderNextPage
    if (memoizedChildren !== displayChildren && readyToRenderNextPage) {
      setDisplayChildren(children)
    }
  }, [memoizedChildren, displayChildren, readyToRenderNextPage])

  return (
    <>
      <StyledPageTransition ref={el} className={pageTransitionClass} />
      {displayChildren}
    </>

}

Upvotes: 2

Views: 576

Answers (0)

Related Questions