Fix Next.js component state not refreshing on navigation
By Flavio Copes
Fix Next.js component state that does not refresh when you navigate by adding key={router.asPath} to the Component in _app.js so React remounts the page.
If your Next.js page keeps stale useState() values when you navigate to another URL, the fix is to add key={router.asPath} to the Component in your custom _app.js. React then remounts the page on every navigation, and state starts fresh.
Here’s how I ran into this. My component had an useState() hook to set some variables, and the state was not updated when navigating with the router.
Turns out my custom _app.js, which I copied from the tutorial and was just used to add global styling to the app, had this code:
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
I changed it to:
import { useRouter } from 'next/router'
export default function App({ Component, pageProps }) {
const router = useRouter()
return <Component {...pageProps} key={router.asPath} />
}
and it worked again as expected.
I just had to add the path as key.
Why does this happen?
This usually shows up with dynamic routes. Say you have a pages/posts/[slug].js page and you navigate from /posts/first-post to /posts/second-post.
Both URLs render the same page component. React sees the same component type in the same position of the tree, so it doesn’t unmount it. It just re-renders it with new props.
And here’s the catch: useState(initialValue) only uses the initial value on the first render. On a re-render, the existing state wins. So any state you derived from the old post sticks around on the new one.
Adding key={router.asPath} changes the key on every URL change. React treats a changed key as a different component, throws away the old instance, and mounts a new one. Fresh mount, fresh state.
The tradeoff
Remounting the whole page on every navigation is a blunt tool. All state in the page is lost, and every useEffect() runs again, including data fetching.
If that’s too much, you can reset just the state you care about instead. Watch the route inside the page component:
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
export default function Post() {
const router = useRouter()
const [comments, setComments] = useState([])
useEffect(() => {
setComments([])
}, [router.asPath])
//...
}
This clears the comments state when the URL changes, without remounting anything else.
For my app the global key was fine, and it’s a one-line fix. Start there, and reach for the targeted reset only if the remount causes you problems.
Related posts about next: