Lee Buckle
Lee Buckle

Reputation: 749

How to NOT persist state between Next.js dynamic routes?

I'm building a headless eCommerce site using React/Next and have a [product].js dynamic route which is used to generate all product pages, using getStaticPaths() and getStaticProps() which generates the pages fine.

I'm using useState hook within [product].js to manage a number input (for quantity) and a couple of other things.

The first product page loaded works fine, but when I go to other product pages, they use the same state from the first product.

Is there a way to have the state NOT persist between route changes?

Through some digging, I found that this is an issue with next and is in their backlog. It essentially stems from the fact that the component doesn't have a key. This means switching between routes on the same dynamic route doesn't register correctly and causes the component to use stale state.

A possible solution I found was this:

export async function getStaticProps({params}) {
    const props = await getData(params);

    // key is needed here
    props.key = data.id; 

    return {
        props: props
    }
}

This is my implementation which doesn't work for me:

export default function ProductPage(props) {

  // this state doesn't reset between dynaic route changes
  const [quantity, setQuantity] = 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
  }
}

I tried wrapping the contents within another component and adding a key to that, like so:

return(
  <OuterComponent key={props.id}>
    // components within here, that have their own state, now work
  </OuterComponent>
)

Since this new keyed component is only in the return statement and does not encapsulate the state hook, it does not work. This does reset the state however, for any components found within wrapped component.

Upvotes: 8

Views: 10884

Answers (3)

You can use useEffect hook and useRouter hook at dynamic router to reset the state.

import {useState, useEffect} from 'react'
import {useRouter} from 'next/router'

const ProductPage = (props) => {
const [state, setState] = useState(someState)
const dynamicRoute = useRouter().asPath
useEffect(() => {
  setState(resetState) // When the dynamic route change reset the state
}, [dynamicRoute])
//Some other logic
return (
......
)
}

Upvotes: 12

Vladimir Vs
Vladimir Vs

Reputation: 1492

You can use useEffect hook to reset state

export default function ProductPage(props) {



// this state doesn't reset between dynaic route changes
  const [quantity, setQuantity] = useState(1)

  useEffect(() => {
    setQuantity(props.quantity) // <-- this props comes from getStaticProps
}, [props]) // <--- useEffect will keep tracking changing props



  return(
    ...
  )
}

So when your props changes - your state updates.

Upvotes: 1

Wiktor Bednarz
Wiktor Bednarz

Reputation: 749

It seems that you've encountered the same issue thread that I've found: https://github.com/vercel/next.js/issues/9992

It seems from what I've read that to fix your case, all you need to do is change your getStaticProps to return an object with a unique key:


export async function getStaticProps({ params }) {
  const slug = params.product
  const props = await client.query({
    query: singleProductQuery,
    variables: { id: slug }
  });

  return {
    props: props,
    key: slug
  }
}

What you've been doing previously is passing a key to the props object instead of root return object for getStaticProps

Upvotes: 6

Related Questions