Gatsby: React conditional rendering based on window.innerWidth misbehaving

泪湿孤枕 提交于 2020-07-06 12:09:40

问题


Conditional rendering of components based on window.innerWidth seems to not work as intended just in the production build of Gatsby based website.

The hook I am using to check the viewport's width, with the additional check for the window global to avoid Gatsby-node production build errors, is the following:

import { useState, useEffect } from 'react'

const useWindowWidth = () => {
  const windowGlobal = typeof window !== 'undefined'

  if(windowGlobal) {
    const [width, setWidth] = useState(window.innerWidth)

    useEffect(() => {
      const handleResize = () => setWidth(window.innerWidth)
      window.addEventListener('resize', handleResize)
      return () => {
        window.removeEventListener('resize', handleResize)
      }
    })

    return width
  }
}

export default useWindowWidth

Then in my actual component I do the following:

IndexPage.Booking = () => {
  const windowWidth = useWindowWidth()

  return (
    <div className="section__booking__wrapper">
      { windowWidth <= mediaQueries.lg && <IndexPage.Cta /> }
      <div className="section__booking-bg" style={{ backgroundImage: `url(${bg})` }}>
        { windowWidth > mediaQueries.lg && <IndexPage.Cta /> }
      </div>
    </div>
  )
}

It works as it should in development but the production build fails to render:

<div className="section__booking-bg" style={{ backgroundImage: `url(${bg})` }}>

When resizing the window below the mediaQueries.lg (1024) it then triggers the actual normal behaviour or conditionally rendering mobile and desktop versions of the component.

To doublecheck if it was because the render triggers on just the resize event (which it doesn't as it works on load in development environment) I also simply, from within the hook console.log() the return value and it gets printed, in production correctly on load.

There are also no errors or warnings in the production or development build whatsoever.

Edit as per @Phillip 's suggestion

const useWindowWidth = () => {
  const isBrowser = typeof window !== 'undefined'
  const [width, setWidth] = useState(isBrowser ? window.innerWidth : 0)

  useEffect(() => {
    if (!isBrowser) return false

    const handleResize = () => setWidth(window.innerWidth)
    window.addEventListener('resize', handleResize)

    return () => {
      window.removeEventListener('resize', handleResize)
    }
  })

  return width
}

It now works just when you resize it, once, under the mediaQueries.lg threshold and then it works flawlessly across desktop and mobile but not on load.


回答1:


I had a similar problem to this and haven't found a solution, but a work around. Put the following at the start of your render:

if (typeof window === `undefined`) {
    return(<></>);
}

What I think is happening is that Gatsby is building the page with a style based off the window width (which will be 0 / undefined). Then it's not updating the style in the DOM once the page loads as it thinks it has already performed that action. I think this is a small bug in Gatsby maybe?

Either way, the above renders your component blank during the build, forcing it to fully respect all logic when the page loads. Hopefully that provides a solution albeit not a satisfying/complete explanation :)




回答2:


Don’t call Hooks inside loops, conditions, or nested functions (from React docs)

React Hooks must run in the exact same order on every render. Move your condition into the useEffect callback:

useEffect(() => {
  if (typeof window === 'undefined') return;

  const handleResize = () => setWidth(window.innerWidth);
  window.addEventListener('resize', handleResize);
  return () => {
    window.removeEventListener('resize', handleResize)
  };
});



回答3:


I'm guessing it is too late to answer but calling handleResize before adding the event listener should work. Here is a code I used for same purpose:

  useEffect(() => {
  setWidth(window.innerWidth);
  window.addEventListener("resize", () => {
    setWidth(window.innerWidth);
  });
  return () => {
    window.removeEventListener("resize", () => {});
  };
}, []);


来源:https://stackoverflow.com/questions/58608523/gatsby-react-conditional-rendering-based-on-window-innerwidth-misbehaving

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!