【问题标题】:Gatsby: React conditional rendering based on window.innerWidth misbehavingGatsby:基于 window.innerWidth 行为不端的 React 条件渲染
【发布时间】:2020-02-24 18:11:15
【问题描述】:

基于 window.innerWidth 的组件条件渲染似乎无法在基于 Gatsby 的网站的 生产 构建中按预期工作。

我用来检查视口宽度的钩子,以及对全局窗口的额外检查以避免 Gatsby 节点生产构建错误,如下所示:

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

然后在我的实际组件中执行以下操作:

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>
  )
}

它在 development 中正常工作,但 production 构建无法呈现:

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

当调整 mediaQueries.lg (1024) 下面的窗口大小时,它会触发实际的正常行为或有条件地呈现组件的移动和桌面版本。

要仔细检查是否是因为渲染仅在 resize 事件上触发(它在 development 环境中加载时不起作用)我也简单地从钩子 console.log() 中返回值并在生产中在加载时正确打印。

productiondevelopment 构建中也没有任何错误或警告。

根据@Phillip 的建议进行编辑

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
}

它现在只在你调整它的大小时工作,一次,低于 mediaQueries.lg 阈值,然后它可以在桌面和移动设备上完美地工作,但不是在加载时。

【问题讨论】:

    标签: javascript reactjs jsx gatsby server-side-rendering


    【解决方案1】:

    我猜现在回答为时已晚,但在添加事件侦听器之前调用 handleResize 应该可以工作。这是我用于相同目的的代码:

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

    【讨论】:

      【解决方案2】:

      我遇到了类似的问题,但没有找到解决方案,但可以解决。将以下内容放在渲染的开头:

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

      我认为正在发生的事情是 Gatsby 正在使用基于窗口宽度(将是 0 / 未定义)的样式构建页面。然后它不会在页面加载后更新 DOM 中的样式,因为它认为它已经执行了该操作。我认为这可能是 Gatsby 中的一个小错误?

      无论哪种方式,以上内容都会在构建过程中将您的组件呈现为空白,从而强制它在页面加载时完全遵守所有逻辑。希望这提供了一个解决方案,尽管不是一个令人满意/完整的解释:)

      【讨论】:

      • 非常感谢,会尝试一下,虽然它给我留下了苦涩的回味啊哈
      • 我遇到了同样的问题(我在这里发布了:stackoverflow.com/questions/59805587/…),这解决了它。谢谢!
      • 它部分对我有用 html 按原样呈现,但 css 未正确加载:(
      • 很好的答案和解释,但 IMO 解决方案可以改进。您不需要在 SSR 期间隐藏整个组件,只需隐藏受window 影响的位即可。这样你仍然可以充分利用 Gatsby。像这样的东西应该在 OP 的情况下起作用:{ typeof window === "undefined" ? &lt;&gt;&lt;/&gt; : windowWidth &lt;= mediaQueries.lg &amp;&amp; &lt;IndexPage.Cta /&gt; }(未经测试,但我确信这个或非常类似的东西会起作用)。
      • 同意@Avius,有更具体的方法可以解决这个问题,从而产生更好的结果。我简化了我的答案,因为我认为它对每个人都更有帮助,作为问题的解释和修复,而不是仅仅修复 OP 的代码 :)
      【解决方案3】:

      不要在循环、条件或嵌套函数中调用 Hooks (from React docs)

      React Hooks 必须每个渲染中以完全相同的顺序运行。将您的条件移动到useEffect 回调中:

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

      【讨论】:

      • 我知道在一个条件下调用这样的钩子有些可疑,但我仍然没有注意这一点。会尝试让您知道,感谢您的帮助!
      • 在我按照我在回答中的编辑修改代码后,同样的情况发生了:现在,在development 中,当您将窗口大小调整到阈值以下时它才起作用,然后桌面和移动设备都按预期工作.
      • 任何线索为什么会这样?
      猜你喜欢
      • 2020-12-09
      • 2019-07-11
      • 1970-01-01
      • 2020-10-22
      • 2018-05-30
      • 1970-01-01
      • 2021-08-16
      • 2021-10-06
      • 1970-01-01
      相关资源
      最近更新 更多