(抬头警告:我对React / GraphQL并不是很熟悉,所以请放轻松我。)
我正在使用Gatsby构建博客,我的导航栏会根据屏幕尺寸更改其外观。我的导航栏中间是博客的徽标。现在,当读者在桌面上查看页面时,我希望显示完整的徽标(包括文本)。当他们在移动设备上查看时,我只希望呈现实际徽标(不包括文字)(我有一幅带有完整徽标的图像和一幅带有缩短徽标的图像)。
到目前为止,我的方法是1.创建两个单独的SiteNavLogo组件,并2.使用以下代码块检查当前窗口的大小(我删除了与该问题无关的所有代码,并将其替换了和“ ...”),并有条件地渲染两个徽标组件之一:
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查询?) 任何建议都将受到高度赞赏。
答案 0 :(得分:2)
您不应使用JS处理此问题,因为它总是会导致渲染不匹配。
例如,如果默认情况下,您在视口宽度小于400px时渲染<Logo>
,但是用户以600px视口宽度访问您的网站,则他们会在React之前看到一小段错误的组件踢进来。
您可以通过检查window
来解决此问题,并且仅在SSR中不呈现任何内容(这是对非SEO敏感内容的一种很好的方法),但是您首先失去了使用盖茨比的好处,搜索引擎看不到您的徽标,并且取决于布局的设置方式,用户仍可能会看到布局的变化。
如果仅使用CSS不能简单地修改您的组件,那么我建议您使用CSS切换器:
<div className="display-xs">
<MobileVariant />
</div>
<div className="display-md">
<DesktopVariant />
</div>
答案 1 :(得分:1)
尝试:
return (
<>
...
{ typeof window !== 'undefined'
? window.innerWidth <= 400 ? <SiteNavLogoMobile /> : <SiteNavLogo/>
: null
}
</>
);
当然,应该对代码进行重构,以避免链式三元条件(可读性差),但是我想展示这种方法的思想。如果定义了window
,它将匹配第一个条件,根据innerWidth
渲染一个或另一个组件(您可以将其更改为matchMedia
),否则将不渲染直到代码获得窗口的null
为止的所有内容(innerWidth
。
您可以将其添加到自定义函数中,而不是根据需要在JSX中呈现它。
此代码将随着窗口宽度的变化而触发。
答案 2 :(得分:0)
您应该使用组件的状态来存储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演示。