Reputation: 304
I have a react hook set up to handle masonry on certain pages of my gatsby site. The problem is it references the window object, which does not exist on the server side gatsby build
. I've read that the solution is to wrap useEffect with:
if (typeof window === 'undefined') {
}
however I just can't seem to wrap the right part of my masonry file. I've also read that using the above hack makes the server side rendering sort of pointless, not sure.
Could someone tell me where that if statement should go in my masonry file below? It's not a plugin, it's a hook in my utils folder. Using code from this tut. I've tried the if statement inside the useEffects, around the useEffects, around the whole eventListener, but no dice. Thank you!!
import React, { useEffect, useRef, useState } from "react"
import styled from "styled-components"
const useEventListener = (eventName, handler, element = window) => {
const savedHandler = useRef()
useEffect(() => {
savedHandler.current = handler
}, [handler])
useEffect(() => {
const isSupported = element && element.addEventListener
if (!isSupported) return
const eventListener = event => savedHandler.current(event)
element.addEventListener(eventName, eventListener)
return () => {
element.removeEventListener(eventName, eventListener)
}
}, [eventName, element])
}
const fillCols = (children, cols) => {
children.forEach((child, i) => cols[i % cols.length].push(child))
}
export default function Masonry({ children, gap, minWidth = 500, ...rest }) {
const ref = useRef()
const [numCols, setNumCols] = useState(3)
const cols = [...Array(numCols)].map(() => [])
fillCols(children, cols)
const resizeHandler = () =>
setNumCols(Math.ceil(ref.current.offsetWidth / minWidth))
useEffect(resizeHandler, [])
useEventListener(`resize`, resizeHandler)
const MasonryDiv = styled.div`
margin: 1rem auto;
display: grid;
grid-auto-flow: column;
grid-gap: 1rem;
`
const Col = styled.div`
display: grid;
grid-gap: 1rem;
`
return (
<MasonryDiv ref={ref} gap={gap} {...rest}>
{[...Array(numCols)].map((_, index) => (
<Col key={index} gap={gap}>
{cols[index]}
</Col>
))}
</MasonryDiv>
)
}
Upvotes: 1
Views: 622
Reputation: 29320
In your gatsby-node.js
add the following snippet:
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === "build-html") {
actions.setWebpackConfig({
module: {
rules: [
{
test: /masonry/,
use: loaders.null(),
},
],
},
})
}
}
Note: use /masonry/
library path from your node_modules
.
From Gatsby documentation about debugging HTML builds:
Errors while building static HTML files generally happen for one of the following reasons:
Some of your code references “browser globals” like window or document. If this is your problem you should see an error above like “
window
is not defined”. To fix this, find the offending code and either a) check before calling the code if window is defined so the code doesn’t run while Gatsby is building (see code sample below) or b) if the code is in the render function of a React.js component, move that code into acomponentDidMount
lifecycle or into auseEffect
hook, which ensures the code doesn’t run unless it’s in the browser.
Alternatively, you can wrap your import your Masonry hook usage inside this statement:
if (typeof window !== `undefined`) {
const module = require("module")
}
Note the !==
comparison, not the ===
like the one you've provided.
Upvotes: 2