首页 > 解决方案 > 使用 GraphQL 在 NextJS 中渲染的钩子比上一次渲染时更多

问题描述

我将导航栏放在我的 _app.js 中,所以我不需要将它插入每个组件中。我的问题是,在我登录后它会输出一个错误Rendered more hooks than during the previous render.并将其指向useQuery(GETCARTDATA

请在这里检查我的代码

const App = ({ Component, pageProps }) => {
    const token = getToken()
    const [isPopUpShow, setPopUpShow] = useState(false)
    const [cartStateData, setCartStateData] = useState([])
    const [isCartOpen, setCartOpen] = useState(false)
    let cartDetailsData
    
    if (token) {
      // eslint-disable-next-line react-hooks/rules-of-hooks
      cartDetailsData = useLazyQuery(GETCARTDATA, {
        variables: {
          page: 1
        },
      })
      // eslint-disable-next-line react-hooks/rules-of-hooks
      useMemo(() => {
        const cartData = get(cartDetailsData.data, 'findCartDetails.orders') || []
        const cartItems = []
        if (cartData.length) {
          cartData.map(
            itm =>
              itm.lineItems.length &&
              itm.lineItems.map(item => cartItems.push(item))
          )
        }
        setCartStateData(cartItems)
      }, [cartDetailsData.data])
    }
  
    return (
      <>
        <div className="app-outer">
          {token ? (
            <ShowroomHeader
              isPopUpShow={isPopUpShow}
              setPopUpShow={setPopUpShow}
              cartStateData={cartStateData}
              cartDetailsData={cartDetailsData}
              token={token}
            />
          ) : (
            <Navbar />
          )}
        </div>
  
        <div className="main">
          <Component {...pageProps} />
        </div>

      </>
    )
  }
  
  export default withApollo(App)

标签: reactjsreact-hooksgraphqlnext.jsapollo

解决方案


正如@xadmn 所提到的,您正在有条件地渲染您的钩子,而 React 期望每次渲染时都有相同数量的钩子调用,从而违反了Hooks 的规则

您需要删除您的 if 语句并将您的条件移动到一个useEffect钩子中,使用useLazyQuery返回的函数从那里执行查询。您还可以将useMemo代码移至onCompleted回调,因为它取决于查询的结果。

const App = ({ Component, pageProps }) => {
    const token = getToken()
    const [isPopUpShow, setPopUpShow] = useState(false)
    const [cartStateData, setCartStateData] = useState([])
    const [isCartOpen, setCartOpen] = useState(false)

    const [getCardData, cartDetailsData] = useLazyQuery(GETCARTDATA, {
        onCompleted: (data) => {
            const cartData = get(data, 'findCartDetails.orders') || []
            const cartItems = []
            if (cartData.length) {
                cartData.map(
                    itm =>
                        itm.lineItems.length &&
                        itm.lineItems.map(item => cartItems.push(item))
                )
            }
            setCartStateData(cartItems)
        }
    })

    useEffect(() => {
        if (token) {
            getCardData({ variables: { page: 1 } })
        }
    }, [token])
  
    return (
        // Your JSX here
    )
}

推荐阅读