React / Gatsby - 根据屏幕大小渲染不同的组件
Posted
技术标签:
【中文标题】React / Gatsby - 根据屏幕大小渲染不同的组件【英文标题】:React / Gatsby - render different component based on screen size 【发布时间】:2021-02-07 21:09:32 【问题描述】:(注意警告:我对 React/GraphQL 不是很熟悉,所以请放轻松)。
我正在使用 Gatsby 构建博客,我的导航栏会根据屏幕大小更改其外观。在我的导航栏中间是博客的标志。现在,当读者在桌面上查看页面时,我希望显示完整的徽标(包括文本)。当他们在移动设备上查看时,我只希望呈现实际徽标(不包括文本)(我有一张带有完整徽标的图像和一张带有缩短徽标的图像)。
到目前为止,我的方法是 1. 创建两个单独的 SiteNavLogo-components 和 2. 使用以下代码块检查当前窗口大小(我删除了所有与此问题无关的代码并替换它使用“...”)和 3. 有条件地渲染两个徽标组件之一:
class SiteNav extends React.Component
...
;
render(): JSX.Element
changeLogo = window.matchMedia('(max-width: 600px)').matches;
return (
<>
...
changeLogo && (
<SiteNavLogoMobile />) || !changeLogo &&
<SiteNavLogo/>
</>
);
这适用于gatsby develop
,但对于gatsby build
则失败,因为“窗口”不可用。在this 的文章中找到了使用componentDidMount 的推荐,所以我尝试了这个:
class SiteNav extends React.Component
changeLogo = false;
componentDidMount(): void
this.changeLogo = window.matchMedia('(max-width: 600px)').matches;
;
render(): JSX.Element
return (
<>
...
this.changeLogo && (
<SiteNavLogoMobile />) || !this.changeLogo &&
<SiteNavLogo/>
</>
);
这样做的问题是,第一个渲染周期是在 componentDidMount 执行之前执行的,这意味着页面将始终使用 changeLogo=false 进行渲染。
我怎样才能使移动用户查看页面时呈现缩短的徽标? (为此我什至需要两个组件还是可以只更改我的 GraphQL 查询?) 非常感谢任何建议。
【问题讨论】:
【参考方案1】:您应该使用组件的状态来存储changeLogo
变量。通过使用状态变量,changeLogo
在componentDidMount()
中正确更新,从而显示正确的导航栏。
为确保changeLogo
根据视口宽度进行更新,组件中还包含addEventListener
。
class SiteNav extends React.Component
constructor(props)
super(props);
this.state =
changeLogo: false
;
this.handleResize = this.handleResize.bind(this);
handleResize()
this.setState(
changeLogo: window.matchMedia("(max-width: 400px)").matches
);
componentDidMount()
window.addEventListener("resize", this.handleResize);
componentWillUnmount()
window.removeEventListener("resize", this.handleResize);
render()
return (
<div>
this.state.changeLogo && <SiteNavLogoMobile />
!this.state.changeLogo && <SiteNavLogo/>
</div>
);
这是一个Codesandbox 演示。
【讨论】:
但是这只渲染一次而不是监听视口宽度? 您的问题并不清楚您是否希望它监听视口宽度。我已经更新了上面的答案以允许这样做。 谢谢你!这种方法对我来说非常有意义,但由于某些奇怪的原因handleResize()
没有被执行。我可能只是犯了一些小错误,导致函数无法执行。我一定会牢记状态的逻辑,以备不时之需!这看起来确实很有用。【参考方案2】:
试试:
return (
<>
...
typeof window !== 'undefined'
? window.innerWidth <= 400 ? <SiteNavLogoMobile /> : <SiteNavLogo/>
: null
</>
);
当然,应该重构此代码以避免链式三元条件(可读性差),但我想展示该方法的想法。如果定义了window
,它将匹配第一个条件,根据innerWidth
渲染一个或另一个组件(您可以将其更改为您的matchMedia
)如果没有,它将不会渲染任何东西(null
)直到代码得到窗口的innerWidth
。
您可以根据需要将其添加到自定义函数中,而不是在 JSX 中呈现它。
此代码将随着窗口宽度的变化而触发。
【讨论】:
谢谢!这是一个非常简单的解决方案,而且效果很好。我真的很喜欢这种方法。【参考方案3】:你不应该用 JS 来处理这个,因为它总是会导致渲染不匹配。
例如,如果默认情况下,当视口宽度小于 400px 时渲染 <Logo>
,但用户以 600px 的视口宽度访问您的站点,他们会在 React 启动之前看到短暂的错误组件.
您可以通过检查window
来解决这个问题,并且只在 s-s-r 中不渲染任何内容(这对于非 SEO 敏感内容来说是一种很好的方法),但是您首先会失去使用 Gatsby 的好处,搜索引擎看不到您的徽标,并且取决于您的布局设置方式,用户可能仍会看到布局转变的闪光。
如果你的组件不能简单地只用 CSS 来修改,我推荐的是好的 ol' CSS switcharoo:
<div className="display-xs">
<MobileVariant />
</div>
<div className="display-md">
<DesktopVariant />
</div>
【讨论】:
谢谢!我刚刚测试了它,它确实闪烁。现在我使用 CSS 对其进行了更改,它不再短暂闪烁。如果没有您的评论,我可能不会注意到它;非常感谢您提出这个问题!以上是关于React / Gatsby - 根据屏幕大小渲染不同的组件的主要内容,如果未能解决你的问题,请参考以下文章
Gatsby 构建失败 - 错误“窗口”在服务器端渲染期间不可用
如何在不渲染子组件的情况下根据子组件大小更改 React 父组件大小?
React Gatsbyjs 根据纵横比向 gatsby-image 添加类