skip to Main Content

I am having a function to set the header height and this is using the window object. Because I am using Gatsby as my SSG, window is not available during build. I had a workaround (down below), but this will give me two errors during gatsby develop.

error React Hook "useWindowSize" is called conditionally. React Hooks must be called in the exact same order in every component render

error React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render

import * as React from "react"

//Library
import { useEffect, useState } from "react"
import { useStaticQuery, graphql } from "gatsby"
import loadable from "@loadable/component"

//Components
import { Header } from "./header"

import { SkipNavContent, SkipNavLink } from "./skip-nav"
import { Seo } from "./seo"
import Toast from "./toast"

// import Cookie from "./cookie"

//Utils
import { StoreContext } from "../context/store-context"
import { isBrowser, useWindowSize } from "../utils/helpers"

const CartSidebar = loadable(() => import("./cart/CartSidebar"))
const Newsletter = loadable(() => import("./newsletter"))
const Footer = loadable(() => import("./footer"))

export function Layout({ children }) {
  const { loading, didJustAddToCart } = React.useContext(StoreContext)
  const [showLazySections, setShowLazySections] = React.useState(false)

  if (typeof window !== "undefined") {
    const { height: windowHeight } = useWindowSize()
    // set header height
    useEffect(() => {
      if (isBrowser) {
        document.body.style.setProperty("--vh", `${windowHeight * 0.01}px`)
      }
    }, [windowHeight])
  }

  const [headerHeight, setHeaderHeight] = useState(null)

  useEffect(() => {
    setShowLazySections(true)
  }, [])

  return (
    <div
      style={headerHeight ? { "--headerHeight": `${headerHeight}px` } : null}
    >
      <SkipNavLink />
      {/* <Cookie siteTitle={siteTitle} /> */}
      <Seo />
      <Header onSetup={({ height }) => setHeaderHeight(height)} />
      <SkipNavContent>{children}</SkipNavContent>
      {showLazySections && (
        <>
          <CartSidebar />
          <Newsletter />
          <Footer siteTitle={siteTitle} />
        </>
      )}
    </div>
  )
}

QUESTION: How can I adjust my code to get and set headerheight, during build, without any window undefined errors.

2

Answers


  1. Hooks cannot be called conditionally, or in loops. They must be at the top level of the component and always called in the same order.

    Refactor

    if (typeof window !== "undefined") {
        const { height: windowHeight } = useWindowSize()
        // set header height
        useEffect(() => {
          if (isBrowser) {
            document.body.style.setProperty("--vh", `${windowHeight * 0.01}px`)
          }
        }, [windowHeight])
      }
    

    to

        const windowSize = useWindowSize()
        const windowHeight = windowSize?.height; // May be null
        // set header height
        useEffect(() => {
          if (isBrowser) {
            document.body.style.setProperty("--vh", `${windowHeight * 0.01}px`)
          }
        }, [windowHeight])
    
    Login or Signup to reply.
  2. A simple way to put the React Hook out of the if statement is to pass in the data you are testing (in this case the window) and test if it is undefined directly inside the Hook. Like with useEffect you gotta Test inside the Hooks and not Test to decide if you have to use the Hooks.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search