【问题标题】:React / Gatsby - render different component based on screen sizeReact / Gatsby - 根据屏幕大小渲染不同的组件
【发布时间】: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 查询?) 非常感谢任何建议。

【问题讨论】:

    标签: html css reactjs graphql gatsby


    【解决方案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 来解决这个问题,并且只在 SSR 中不渲染任何内容(这对于非 SEO 敏感内容来说是一种很好的方法),但是您首先会失去使用 Gatsby 的好处,搜索引擎看不到您的徽标,并且取决于您的布局设置方式,用户可能仍会看到布局转变的闪光。

    如果你的组件不能简单地只用 CSS 来修改,我推荐的是好的 ol' CSS switcharoo:

    <div className="display-xs">
      <MobileVariant />
    </div>
    
    <div className="display-md">
      <DesktopVariant />
    </div>
    

    【讨论】:

    • 谢谢!我刚刚测试了它,它确实闪烁。现在我使用 CSS 对其进行了更改,它不再短暂闪烁。如果没有您的评论,我可能不会注意到它;非常感谢您提出这个问题!
    猜你喜欢
    • 2020-10-22
    • 1970-01-01
    • 2015-03-06
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    • 2021-03-19
    • 2020-11-07
    • 2013-10-26
    相关资源
    最近更新 更多