The page state is not reset for navigation between dynamic routes that served by the same source component.
for example, give page source /a/[param]/index.js
, when navigating from /a/1
to /a/b
, states on the page won't' be reset.
The causing is that for this kind of navigation, what actually happened is the same React Component been rendered with different props
. Thus react takes it as a component is rerendering itself, and causing the new navigated page receive stale states.
To fix it, just add {key: <composed base on URL routing params> }
to page initial props.
I've created a live example:
Page should be fully reloaded and states should be reset.
N/A
Yes! I've seen similar confusing behavior when using useEffect
.
React.useEffect(() => {
console.log("page initial render");
}, []);
It only fires once, even when you change the page, but stay on the same route. I'm not sure if it's wrong, but it's definitely not intuitive in my opinion. It's non-intuitive because it works differently when changing the page, but coming from a different route.
@BananaWanted I just got this exact issue and thanks to @Janpot he pointed me to this post and tried your solution and it worked for me.
I'm not sure if this is a "next router" issue or a react issue, but even with your workaround/solution sometimes the browser back button doesn't work as expected, so I think this should be fixed.
Great find, suggest docs are updated. For future devs, same should be applied in getStaticProps()
export async function getStaticProps({params}) {
const props = await getData(params);
// key is needed here
props.key = data.id;
return {
props: props
}
}
Thanks for the example with the key props. Makes me think of a resource I read recently from Kent C Dodds on the topic : https://kentcdodds.com/blog/understanding-reacts-key-prop
For my need, I have made a bespoke key based on the current timestamp, to ensure to always have a new value :
MyPage.getInitialProps = (ctx) => {
const { id } = ctx.query
if (!id || isNaN(id)) return { initialUser: {}, key: Number(new Date()) }
}
Confirming that
Page.getInitialProps = (c) => {
return {
id: String(c.query.id),
key: String(c.query.id),
};
};
Produced the expected mount/unmount effects that @Janpot mentioned.
Any solution?
I have this issue also. The suggested solution (adding a key) works for me in development builds but not in full builds.
Any idea why that could be ?
I also ran into this problem. Adding key to getStaticProps did the trick. I was able to get this solution to build for production with no issues.
Great find, suggest docs are updated. For future devs, same should be applied in
getStaticProps()
export async function getStaticProps({params}) { const props = await getData(params); // key is needed here props.key = data.id; return { props: props } }
where is data come from ? i dont get it
It's wherever you get data from in your implementation. As I understand any unique string could be used in lieu of data.id (though it's still not actually working for me in production, I need to revisit).
It's wherever you get data from in your implementation. As I understand any unique string could be used in lieu of data.id (though it's still not actually working for me in production, I need to revisit).
i got something like this, can i put key with data instead of 'prop' example of samuel ?
export async function getStaticProps({ params }) {
const {data} = await comercioBySlug(params.slug)
return{
props: {data},
unstable_revalidate: 10
}
}
seems like this may be happening in my app even when the dynamic part is in the middle of a route users/[id]/analytics
-> users/[id]/settings
thanks for posting a workaround
export async function getStaticProps({params}) { const props = await getData(params); // key is needed here props.key = data.id; return { props: props } }
I'm using Next version 9.4.4 and this solution isn't working for me
@modulizer-lee key
needs to be at the root of the object. You may mean to return props
.
@tmikeschu sorry, I'm a little confused about what you mean. What I currently have is this:
export default function ProductPage(props) {
const [something, setSomething] = useState(1)
return(
...
)
}
export async function getStaticProps({ params }) {
const slug = params.product
const props = await client.query({
query: singleProductQuery,
variables: { id: slug }
})
props.key = props.data.product.slug
return {
props: props
}
}
Does this look wrong?
I figured another solution to this problem would be to simply enclose the contents of the page within a simple layout component and add a key to that, but it only fixes states within contained components and not the state defined outside, like in my example above.
@modulizer-lee pardon me. I hadn't seen the version change and the preference for getStaticProps
. I had getInitialProps
in my head, in which the return value itself is the component props, without the props
key on the returning object.
OMG this keyed solution is magic!
But shouldn't this be the default behavior for dynamic routes?
In my blog post page I had some "refresh" issues navigation client side and I didn't understood why... until now :P
Hello, any workaround for this issue with getStaticProps ?
The problem with using the key prop in getStaticProps is that, while going from one page with dynamic props to another one with different props will work, going back (using window.history.back()) will only show the last page with dynamic props that was reached.
/a/1 ---> /a/2 ---> /a/3 ---> /a/4 ---> /a/5
This will work ok with key in props.
/a/5 <--- /a/5 <--- /a/5 <--- /a/5 <--- /a/5
This is how the pages will render when using history.back() while having key in props.
Most helpful comment
Great find, suggest docs are updated. For future devs, same should be applied in
getStaticProps()