Search code examples
reactjsreact-hooksgraphqlnext.jsapollo

Rendered more hooks than during the previous render in NextJS using GraphQL


I'm putting navbar in my _app.js so I don't need to insert it in every component. My problem is that after I login it outputs an error Rendered more hooks than during the previous render. and its pointing it on useQuery(GETCARTDATA

Pls check my code here

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)

Solution

  • As @xadmn mentioned, you're rendering your hooks conditionally while React expects the same number of hook calls on every render, thus breaking the rules of Hooks.

    You'll need to remove your if statement and move your condition inside a useEffect hook, using useLazyQuery's returned function to execute the query from there. You can also move your useMemo code to the onCompleted callback, since it depends on the results from the query.

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