Andreas
Andreas

Reputation: 188

Can't use document in gatsby build, must convert to hooks

I have a script that works fine in the gatsby dev server but when trying to run gatsby build I get an error stating

"document" is not available during server side rendering.

The error fires on this code snippet

const useActiveElement = () => {
  const [active, setActive] = useState(document.activeElement)

I assume that this error is also present any place I use document in this file, maybe outside of the useEffect places I use document. However, I have not been able to properly convert the code to using react hooks in a way that would allow Gatsby to build. Any suggestions on what I should do?

The full file (minus imports)

const useActiveElement = () => {
  const [active, setActive] = useState(document.activeElement)

  const handleKeyup = (e) => {
    if (e.keyCode === 9) setActive(document.activeElement)
  }

  useEffect(() => {
    document.addEventListener("keyup", handleKeyup)
    return () => {
      document.removeEventListener("keyup", handleKeyup)
    }
  }, [])

  return active
}

const Layout = ({
                  children,
                  crumbLabel,
                  subStatus,
                  parentPageLabel,
                  parentPageLink,
                }) => {
  const focusedElement = useActiveElement()

  const data = useStaticQuery(graphql`
      query SiteTitleQuery {
          sanitySiteSettings {
              title
          }
      }
  `)

  useEffect(() => {
    const prevTabbedElements = document.getElementsByClassName("tabbed")
    for (let i = 0; i < prevTabbedElements.length; i++) {
      prevTabbedElements[i].classList.remove("tabbed")
    }
    focusedElement.value && focusedElement.value.classList.add("tabbed")
    focusedElement.classList.add("tabbed")
  }, [focusedElement])


  return (
    <>
      <Header siteTitle={data.sanitySiteSettings.title}/>
      <nav>
        {subStatus ? (
          <Parentcrumbs
            crumbLabel={crumbLabel}
            parentPageLabel={parentPageLabel}
            parentPageLink={parentPageLink}
          />
        ) : (
          <Breadcrumbs crumbLabel={crumbLabel}/>
        )}
      </nav>
      <main>{children}</main>

      <Footer/>
    </>
  )
}
Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout

Upvotes: 3

Views: 2354

Answers (1)

Ferran Buireu
Ferran Buireu

Reputation: 29320

At the initial render point, your document is not defined yet so:

const useActiveElement = () => {
  const [active, setActive] = useState('')

  const handleKeyup = (e) => {
    if (e.keyCode === 9) setActive(document.activeElement)
  }

  useEffect(() => {
    setActive(document.activeElement);

    document.addEventListener("keyup", handleKeyup)
    return () => {
      document.removeEventListener("keyup", handleKeyup)
    }
  }, [])

  return active
}

Init the useState as empty and fill in in your componentDidMount (useEffect with empty deps).

Upvotes: 4

Related Questions