question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Best strategy for checking whether (typeof window !== undefined) while using Hooks

See original GitHub issue

I am using a custom hook to access breakpoint information, which is using the window property, so I’m running into trouble when building.

I am calling the hook from a component like so: const breakpoint = useBreakpoints(typeof window !== undefined)

and here is the code I have for the hook itself:

const getDeviceConfig = (width) => {
  if (width >= 320 && width < 375) {
    return 'xs';
  } else if(width >= 375 && width < 768 ) {
    return 'sm';
  } else if(width >= 768 && width <= 1024) {
    return 'md';
  } else if(width > 1024) {
    return 'lg';
  }
};

const useBreakpoint = (isBrowser) => {
  const [brkPnt, setBrkPnt] = useState(() => getDeviceConfig(isBrowser && window.innerWidth));
  
  useEffect(() => {
    const calcInnerWidth = () => {
      setBrkPnt(getDeviceConfig(isBrowser && window.innerWidth))
    }; 
    isBrowser && window.addEventListener('resize', calcInnerWidth);
    return () => isBrowser &&  window.removeEventListener('resize', calcInnerWidth);
  }, []);

  return brkPnt;
}

export default useBreakpoint

The reason I am passing the (typeof window !== undefined) value from the components to the hook is because of the “Rules of Hooks”: “Don’t call Hooks inside loops, conditions, or nested functions”.

This works, but results in a very weird behavior on deployed version where the page (on desktop mode) loads to the mobile layout and needs a couple of refreshes to correct itself.

Having done some reading in similar issues and answers I now understand a better way to go about it is to do something like this:

const isBrowser = typeof window !== `undefined`

// Function Component with renturned JSX only
const myComponent = (props) => (
  isBrowser && <myActualComponent data={props.data} />
)

but I want to understand exactly what is happening that triggers this weird behavior on deploy?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
polarathenecommented, Aug 14, 2020

(typeof window !== undefined) is always going to be the same value from the first time it’s encountered(either during SSR or on the client), it’s a global, so no need to pass it in as a value, just include it with your hook where it’s used out of it’s scope and reference it as you do with isBrowser, gatsby-image does this.

Having done some reading in similar issues and answers I now understand a better way to go about it is to do something like this

Yes, as I showed in that snippet, but if it only matters for your hook as a condition, you don’t need to wrap it like my example snippet shows there. That was for third-party imported components that were causing problems because they internally use window and don’t have support for SSR otherwise.

This works, but results in a very weird behavior on deployed version where the page (on desktop mode) loads to the mobile layout and needs a couple of refreshes to correct itself.

SSR will happen with a minimal viewport afaik, so expect anything that might bake in inline CSS or markup/components based on width to favor mobile breakpoints.

You can also use the window.matchMedia API for a listener that does similar to what you’re doing here (I have an active PR doing such with gatsby-image, but not hooks based), there’s also a package react-media I think that achieves similar if that suits you, can’t recall but it might also have a hook.

For the lack of updating/responsiveness in the situation you describe, it can be due to hydration. React hydrates from the SSR html it first loads, and assumes the state it would compute would match the HTML it received, and doesn’t bother to check for a mismatch, so you need to trigger a re-render that would alter the returned JSX if I recall… I have a basic PR here for gatsby-image to handle a similar issue.

If you instead get a momentary flicker, that’d be from the SSR output before React/JS kicks in, and may require additional handling, I have a PR showing that fix off too here.

0reactions
polarathenecommented, Aug 16, 2020

That’s a bit odd, since that’s a hydration issue not an SSR one where isBrowser guard would work better. Good that it resolved your issue though.

Read more comments on GitHub >

github_iconTop Results From Across the Web

When window can be undefined? - Stack Overflow
This condition if (typeof window === "undefined") {}. is a typical check that your code is executed in a browser. A browser has...
Read more >
How to solve "window is not defined" errors in React and Next.js
First solution: typeof. While you can't use: if (window !== undefined) { // browser code }. Because this would try to compare a...
Read more >
Using window in React SSR: The Complete Guide
To get around this, we can check if window is available before using it, by running something like if (typeof window === "undefined")...
Read more >
Gatsby - the window is not defined error, what and how to fix it?
You can check if the browser-global object window is defined and based on it, execute the related code block. Copy. const isBrowser =...
Read more >
Why Is Window Not Defined In NextJS? - Frontend Digest
But why is window undefined? · 1. Use the useEffect hook · 2. Check the environment · 3. Use dynamic imports.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found