I have a header.js component, which is present on each page in my next.js/react project.
Inside I've created a typewriter effect with its rotation, so to fire it up I've put the code inside useEffect as:
export default function Header () {
useEffect(() => {
let delay = 2200;
let input = document.getElementsByClassName('myButton')[0],
index = 0;
const cycleText = () =>{
let randomItem = items[Math.floor(Math.random() * items.length)];
button.setAttribute('value', "Click " + randomItem);
index++;
(index === items.length) ? index = 0 : "";
setTimeout(cycleText, delay);
}
cycleText();
}, []);
return ( ...
To recreate sort of document.ready as you'd do with jQuery, altough I'm encountering two issues doing so with useEffect, one is that if I change page, the header remains the same but the effect code looks dead (no text is shown anymore), two, if I go back to the homepage, where the code was working ok on first load, now it shows two instances of the rotation in quick success (as if there are two useEffect going at the same time one above the other, or something similar).
I'm getting closer to understand how useEffect works, I know the [] makes it run once upon page load, but I thought that was all I needed for my case, and looks like I'm wrong. Should I pass something inside [] which can fix both the issues explained above? I've tried with next.js router, passing router.pathname, to let it run again every time there's a page change, but doesn't work.
UPDATE: I've noticed the two strange behaviour described above only happen when going from the homepage to a pre-generated page (via staticPaths and staticProps) and back. If I go from the homepage to the user dashboard (serverside rendered) and back it doesn't mess up with the useEffect in the header
UPDATE 2: I found out it's the retain scroll position code I have inside _app.js which indeed targets that pre-generated page, as well as the homepage, but NOT the user dashboard page in fact. Now I have to figure out how I can retain the scroll position by also letting components to re-render, cause I've seen due to the same issue also another piece of code to make the sidebar sticky doesn't fire up
this is my _app.js code:
import React, { useRef, useEffect, memo } from 'react'
import { Provider as NextAuthProvider } from 'next-auth/client'
import { SWRConfig } from 'swr'
import Router, { useRouter } from 'next/router'
import '../public/style.scss'
import { AnimatePresence } from "framer-motion"
export default function MyApp ({ Component, pageProps }) {
const router = useRouter()
const retainedComponents = useRef({})
var isRetainableRoute = false
if (router.pathname == '/' || router.asPath.includes('/author/') || router.asPath.includes('/video/')){
isRetainableRoute = true
}
// 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 (
<NextAuthProvider
options={{
clientMaxAge: 60,
keepAlive: 61
}}
session={pageProps.session}
>
<SWRConfig
value={{
revalidateOnFocus: false
}}
>
<AnimatePresence exitBeforeEnter>
<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>
</AnimatePresence>
</SWRConfig>
</NextAuthProvider>
)
}