0

我根据指定的断点有条件地在我的页面上呈现某些组件。当屏幕宽度小于该断点时,我改为渲染具有不同功能的组件的移动版本,其中一个附加了一些 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上述样式表中的类将应用到.MobileSidebardiv,并且左边距动画导致组件从左侧滑出。这工作得很好。

但是,我决定将所有这些逻辑抽象到一个自定义钩子中,该钩子接受断点并返回相应的组件,以便可以更轻松地在应用程序的其他页面上使用它,而无需重复相同的逻辑:

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,但我不确定。有任何想法吗?

4

0 回答 0