NextJS中React Portal侧边栏点击关闭按钮动画失效求助
NextJS中React Portal滑出菜单关闭动画失效问题
我在NextJS项目中用React Portal实现了从屏幕右侧滑出的全屏菜单,采用CSS @keyframes实现显隐动画,菜单显示动画正常,但关闭时存在异常:
- 通过Esc键、菜单选项关闭时动画正常
- 点击原打开按钮关闭时动画失效
考虑到Portal在状态变为false时会直接删除内容,我选择为内部容器添加动画,通过点击事件触发动画后用setTimeout延迟调用关闭方法,但推测可能是组件间两个useState状态分配不当导致问题。以下是Header、SlideScreen、Nav三个组件的完整代码,请求排查问题。
// header.js import React, { useState, useEffect } from "react"; import Link from "next/link"; import ThemeToggle from "@/components/themeSwitcher"; import SlideScreen from "@/components/global/slideScreen"; import styles from "@/styles/header.module.css" export default function Header() { const headerInnerClasses = [styles.headerInner, "wrapper"].join(" ") const [showSlideScreen, setShowSlideScreen] = useState(false); const handleClick = () => { setShowSlideScreen(!showSlideScreen); }; const menuBtnClasses = [showSlideScreen ? `${styles.menuBtnActive} ${styles.menuBtn}` : `${styles.menuBtn}`, "mainBtn"].join(" ") useEffect(() => { document.body.className = showSlideScreen ? "isSlideActive" : ""; }); return ( <header className={styles.header}> <div className={headerInnerClasses}> <Link className={styles.headerLogo} href='/'> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 160"><path d="M10 20 0 29.9v-5.5L5 20zM28 20 0 46.9v-5.5L22 20zM40 27 0 64.9v-5.5L40 21zM40 44 0 81.9v-5.5L40 38zM40 61 0 98.9v-5.5L40 55zM40 78 0 115.9v-5.5L40 72zM40 95 0 132.9v-5.5L40 89zM40 112 0 149.9v-5.5L40 106zM40 129 7 160H1l39-37zM40 146l-15 14h-6l21-20zM40 160h-3l3-3z" fill="#494949"/><path d="M128 107.7 69.7 73.9l58.3-34V0L0 73.9 88 126v34h40z" fill="#f65f59"/></svg> </Link> <div className={styles.headerActions}> <ThemeToggle/> <button className={menuBtnClasses} onClick={handleClick}> <div className={styles.burger}></div> </button> </div> </div> {showSlideScreen && <SlideScreen onClose={() => setShowSlideScreen(false)} showSlideScreen={showSlideScreen} />} </header> ) }
// slideScreen.js import React, { useEffect, useState, useRef } from "react"; import ReactDOM from "react-dom"; import styles from "@/styles/slideScreen.module.css" import Nav from "@/components/nav"; //import { CSSTransition } from "react-transition-group"; const SlideScreen = ({onClose}) => { const [isBrowser, setIsBrowser] = useState(false); const [endAnimation, setEndAnimation] = useState(false); const checkIfClickedEsc = e => { if ( e.code === "Escape" ) { setEndAnimation(true); setTimeout(() => { onClose() }, 100) } } const handleCloseClick = () => { setEndAnimation(true); setTimeout(() => { onClose(); }, 100) }; useEffect(() => { setIsBrowser(true); document.addEventListener("keydown", checkIfClickedEsc) return () => { document.removeEventListener("keydown", checkIfClickedEsc) } }, []); const inScreenClasses = [endAnimation ? `${styles.inScreen} ${styles.close}` : `${styles.inScreen}`, "dotsBg"].join(" ") const slideScreenContent = ( <div className={styles.slideScreen}> <div className={inScreenClasses}> <div className="wrapper"> <Nav handleCloseClick={handleCloseClick} /> </div> </div> </div> ) if (isBrowser) { return ReactDOM.createPortal( slideScreenContent, document.getElementById("modal-root") ) } else { return null; } } export default SlideScreen;
// nav.js import React, { useState } from "react"; import Link from "next/link"; import {useRouter} from "next/router"; import styles from "@/styles/header.module.css" const Nav = ({handleCloseClick}) => { const router = useRouter(); const routes = ['Blog', 'Work', 'Contact']; const routesRu = ['Блог', 'Работы', 'Контакты']; return ( <nav className={styles.headerNav}> <ul className={styles.headerMenu}> {routes.map((route, i) => { return ( <li key={route} className={`${router.pathname === `/${route.toLowerCase()}`}`}> <Link href={`/${route.toLowerCase()}`} onClick={handleCloseClick} >{routesRu[i]}</Link> </li> ) })} </ul> </nav> ) } export default Nav
内容的提问来源于stack exchange,提问作者Лео
相关产品推荐
相关产品推荐

