盖茨比:基于window.innerWidth的React条件渲染行为不端



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

我用来检查视口宽度的钩子,以及为避免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中正常工作,但生产构建无法呈现:

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

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

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

生产development内部版本中也没有任何错误或警告。

根据@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阈值下调整它的大小时,它就可以工作了,然后它可以在桌面和移动设备上完美地工作,但不能在加载时工作。

我遇到了类似的问题,还没有找到解决方案,但有一个解决方案。在渲染开始时放置以下内容:

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

我认为正在发生的事情是,Gatsby正在使用基于窗口宽度的样式构建页面(该样式将为0/undefined(。然后,一旦页面加载,它就不会更新DOM中的样式,因为它认为自己已经执行了该操作。我想这可能是盖茨比的一个小虫子吧?

无论哪种方式,上面的内容都会在构建过程中使组件为空,迫使它在加载页面时完全尊重所有逻辑。希望这能提供一个解决方案,尽管不是一个令人满意/完整的解释:(

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

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

不要在循环、条件或嵌套函数内调用Hook(来自React文档(

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

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

最新更新