我根据指定的断点有条件地在我的页面上呈现某些组件。当屏幕宽度小于该断点时,我改为渲染具有不同功能的组件的移动版本,其中一个附加了一些 CSS 动画。我最初为页面组件内部的渲染构建了所有逻辑,并且一切正常:
页面组件:
import React, { useState, useEffect } from 'react';
import { MobileHeader, MobileSidebar } from '%COMPONENTS%/app';
import { Header } from '../../../molecules/proposal';
import { Sidebar } from '../../../organisms';
import { LibraryScreen } from '../libraryScreen';
import styles from './libraryTemplate.module.scss';
const LibraryTemplateComponent: React.FC = () => {
const { LibraryTemplate, LibraryTemplate__MainSection, mobile } = styles;
const [sidebarVisible, setSidebarVisible] = useState<boolean>(false);
const [sidebarRendered, setSidebarRendered] = useState<boolean>(false);
const [windowWidth, setWindowWidth] = useState<number>(0);
const breakpoint: number = 800;
const isDesktop = (): boolean => windowWidth > breakpoint;
useEffect(() => {
setWindowWidth(window.innerWidth);
window.addEventListener('resize', () => setWindowWidth(window.innerWidth));
return () => window.removeEventListener('resize', () => setWindowWidth(window.innerWidth));
}, []);
const handleOpen = (): void => {
setTimeout(() => setSidebarVisible(true), 10);
setSidebarRendered(true);
};
const handleClose = (): void => {
setTimeout(() => setSidebarRendered(false), 300);
setSidebarVisible(false);
};
return (
<div className={LibraryTemplate}>
{ isDesktop() ? <Header /> : <MobileHeader handleClick={handleOpen} /> }
<main className={`${LibraryTemplate__MainSection} ${!isDesktop() && mobile}`}>
{ isDesktop()
? <Sidebar />
: sidebarRendered
&& <MobileSidebar handleClick={handleClose} isVisible={sidebarVisible} /> }
<LibraryScreen />
</main>
</div>
);
};
export default LibraryTemplateComponent;
侧边栏动画的样式:
.MobileSidebar {
position: fixed;
display: flex;
flex-direction: column;
width: 12rem;
align-items: flex-start;
background-color: $grey-200;
height: 100%;
z-index: 2;
@include lightShadow40;
top: 0;
left: 0;
margin-left: -13rem;
transition: margin-left 300ms ease;
}
.open {
margin-left: 0rem;
}
基本上,当在移动布局中单击按钮时,open
上述样式表中的类将应用到.MobileSidebar
div,并且左边距动画导致组件从左侧滑出。这工作得很好。
但是,我决定将所有这些逻辑抽象到一个自定义钩子中,该钩子接受断点并返回相应的组件,以便可以更轻松地在应用程序的其他页面上使用它,而无需重复相同的逻辑:
useResponsiveLayout 自定义钩子:
import { useState, useEffect } from "react";
import { MobileHeader, MobileSidebar } from '%COMPONENTS%/app';
import { Header as DesktopHeader } from '%COMPONENTS%/molecules';
import { Sidebar as DesktopSidebar } from '%COMPONENTS%/organisms';
export const useResponsiveLayout = (
breakpoint: number,
) => {
const [windowWidth, setWindowWidth] = useState<number>(0);
const [sidebarVisible, setSidebarVisible] = useState<boolean>(false);
const [sidebarRendered, setSidebarRendered] = useState<boolean>(false);
useEffect(() => {
setWindowWidth(window.innerWidth);
window.addEventListener('resize', () => setWindowWidth(window.innerWidth));
return () => window.removeEventListener('resize', () => setWindowWidth(window.innerWidth));
}, []);
const handleOpen = (): void => {
setTimeout(() => setSidebarVisible(true), 50);
setSidebarRendered(true);
};
const handleClose = (): void => {
setSidebarVisible(false);
setTimeout(() => setSidebarRendered(false), 300);
};
const isDesktop: boolean = windowWidth > breakpoint;
const Header = () => isDesktop ? <DesktopHeader /> : <MobileHeader handleClick={handleOpen} />
const Sidebar = () => <div>{isDesktop ? <DesktopSidebar /> : sidebarRendered && <MobileSidebar handleClick={handleClose} isVisible={sidebarVisible} />}</div>;
return { isDesktop, Header, Sidebar };
};
export default useResponsiveLayout;
并且实现了钩子的页面组件:
import React from 'react';
import { LibraryScreen } from '../libraryScreen';
import { useResponsiveLayout } from '%HOOKS%/useResponsiveLayout';
import styles from './libraryTemplate.module.scss';
const LibraryTemplateComponent: React.FC = () => {
const { LibraryTemplate, LibraryTemplate__MainSection, mobile } = styles;
const { isDesktop, Header, Sidebar } = useResponsiveLayout(800);
return (
<div className={LibraryTemplate}>
<Header />
<main className={`${LibraryTemplate__MainSection} ${!isDesktop && mobile}`}>
<Sidebar />
<LibraryScreen />
</main>
</div>
);
};
export default LibraryTemplateComponent;
这完全符合我的要求,除了现在,侧边栏不会根据动画滑出,而是在点击时出现和消失。我的猜测是,它与钩子如何返回组件有关,并且在返回的组件 + 渲染和应用类以触发转换之间存在某种时间上的脱节.open
,但我不确定。有任何想法吗?