【问题标题】:How to NOT persist state between Next.js dynamic routes?如何不在 Next.js 动态路由之间保持状态?
【发布时间】:2020-11-18 10:30:11
【问题描述】:

我正在使用 React/Next 构建一个无头电子商务网站,并有一个用于生成所有产品页面的 [product].js 动态路由,使用 getStaticPaths() 和 getStaticProps() 生成页面正常。

我在[product].js 中使用useState 挂钩来管理数字输入(用于数量)和其他一些事情。

加载的第一个产品页面工作正常,但是当我转到其他产品页面时,它们使用与第一个产品相同的状态。

有没有办法让状态在路由更改之间不存在?

通过一些挖掘,我发现这是 next 的问题,并且在他们的积压中。它本质上源于组件没有密钥的事实。这意味着在同一动态路由上的路由之间切换不会正确注册并导致组件使用陈旧状态。

我找到的一个可能的解决方案是:

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

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

    return {
        props: props
    }
}

这是我的实现,对我不起作用:

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
  }
}

我尝试将内容包装在另一个组件中并为其添加一个键,如下所示:

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

由于这个新的keyed组件只是在return语句中,并没有封装状态钩子,所以不起作用。但是,对于在包装组件中找到的任何组件,这确实会重置状态。

【问题讨论】:

    标签: javascript reactjs react-hooks next.js


    【解决方案1】:

    您可以在动态路由器上使用useEffect hook 和useRouter hook 来重置状态。

    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 (
    ......
    )
    }
    

    【讨论】:

      【解决方案2】:

      您似乎遇到了与我发现的相同的问题线程: https://github.com/vercel/next.js/issues/9992

      从我读到的内容看来,要解决您的问题,您需要做的就是更改您的 getStaticProps 以返回具有唯一键的对象:

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

      您之前所做的是将密钥传递给 props 对象,而不是 getStaticProps 的根返回对象

      【讨论】:

      • 我曾想过这个,但它似乎与 SSG 的观点不一致。我还将当前可见的评论存储在状态中,我担心在页面加载后设置状态可能不利于 SEO。我可以使用 onChange 事件而不是状态,但我想我会首先研究一种有状态的方法。另外,是否有理由在 useEffect 中返回一个函数,而不是简单地在第一个封闭函数中直接调用 setQuantity?
      • 我尝试仅针对数量实施此解决方案,但在动态路由之间切换时不会触发 useEffect。我继续并为我的问题添加了更多细节。
      • 我会试着调查一下。但是查看您的页面代码会有很大帮助。此外,如果您有自定义 _App 或 _Document,也请显示它。然而,它看起来更像是你描述的下一个错误
      • 我将在最初的问题中添加更详细的代码并解释我的位置。感谢您的帮助!
      • 我已经更新了我的答案,它应该可以按照我所看到的那样工作。
      【解决方案3】:

      你可以使用 useEffect 钩子来重置状态

      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(
          ...
        )
      }
      

      所以当你的道具改变时 - 你的状态会更新。

      【讨论】:

        猜你喜欢
        • 2019-02-09
        • 2018-11-25
        • 2018-11-19
        • 2019-02-28
        • 2022-07-14
        • 2018-12-19
        • 2015-12-04
        • 1970-01-01
        • 2022-11-12
        相关资源
        最近更新 更多