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 变量。通过使用状态变量,changeLogocomponentDidMount() 中正确更新,从而显示正确的导航栏。

为确保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 时渲染 &lt;Logo&gt;,但用户以 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 父组件大小?

如何在 Gatsby 中使用 React.lazy

React Gatsbyjs 根据纵横比向 gatsby-image 添加类

根据渲染的背景图片更改 barStyle React Native

标题组件根据 React / Gatsby / Styled-components 中的背景更改文本颜色