You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击菜单关闭按钮后state未置为false,NavBar关闭动画失效

问题根源

核心问题是条件渲染导致组件直接被卸载:不管是Header还是Home组件里,你都用了{asideBar && <NavBar />}的写法——当点击关闭按钮把asideBar设为false时,NavBar会立刻从DOM中消失,关闭动画还没来得及跑完就没了,自然看不到动画效果。

另外,你的动画逻辑完全依赖组件存在于DOM中,组件一被移除,动画就无法执行。

修复方案

方案1:始终保留NavBar组件,用state控制显示/隐藏(最简单)

把条件渲染改成始终渲染NavBar,通过传入的state控制类名触发动画,组件不会被卸载,动画能完整执行。

修改Header组件的渲染逻辑:

export function Header() {
    const [asideBar, setAsideBar] = useState(false)

    return (
        <header
            className="fixed top-0 left-0 right-0 w-screen h-20 p-7 z-20 bg-black overflow-hidden"
        >
            <nav className="flex flex-row justify-between items-center text-customGray pr-4">
                <div>
                    <button onClick={() => setAsideBar(true)} className="mr-4">
                        {/* <MenuIcon /> */}
                    </button>
                    {/* 删掉条件判断,直接渲染NavBar */}
                    <NavBar state={asideBar} setStateFunction={setAsideBar} />
                    <button>
                        {/* <SearchIcon /> */}
                    </button>
                </div>
                <span className="ms-madi-regular tracking-wider text-2xl -rotate-12">WebWish</span>
                <div>
                    <button className="mr-3">
                        <a href="/auth/login">
                            {/* <LoginIcon /> */}
                        </a>
                    </button>
                    <button>
                        {/* <ShopIcon /> */}
                    </button>
                </div>
            </nav>
        </header>
    )
}

同样修改Home组件:

export default function Home() {
  const [asideBar, setAsideBar] = useState(false);

  return (
    <main
      className={`flex min-h-screen flex-col items-center justify-between p-24 ${inter.className}`}
    >
      <button onClick={() => setAsideBar(true)} className="mr-4">
        Open the sidebar
      </button>

      <button onClick={() => setAsideBar(false)} className="mr-4">
        close sidebar
      </button>

      {/* 直接渲染NavBar,不用条件判断 */}
      <NavBar state={asideBar} setStateFunction={setAsideBar} />
    </main>
  );
}

方案2:监听动画结束事件,完成后再卸载组件(适合需彻底移除组件的场景)

如果不想让NavBar一直留在DOM里,可以监听动画结束事件,等关闭动画跑完后再更新state卸载组件:

修改NavBar组件:

import CloseButton from "./CloseButton"
import { useEffect, useState, useRef } from "react"

export const NavBar = ({ state, setStateFunction }: { state: boolean, setStateFunction: (value: boolean) => void }) => {
    const navRef = useRef<HTMLDivElement>(null);
    // 用本地state控制组件显示,避免父组件直接卸载
    const [isVisible, setIsVisible] = useState(state);

    useEffect(() => {
        if (state) {
            // 打开时直接显示
            setIsVisible(true);
        } else {
            // 关闭时先触发动画,结束后再隐藏
            const handleAnimationEnd = () => {
                setIsVisible(false);
                setStateFunction(false);
            };

            const element = navRef.current;
            if (element) {
                element.addEventListener('animationend', handleAnimationEnd);
                return () => element.removeEventListener('animationend', handleAnimationEnd);
            }
        }
    }, [state, setStateFunction]);

    if (!isVisible) return null;

    return (
        <div 
            ref={navRef}
            className={`w-[370px] bg-black fixed top-0 left-0 z-40 h-screen transform transition-transform duration-1000 ${state === false ? 'animate-translateX0' : 'animate-translateX'}`}
        >
            <button className="absolute top-5 right-5 font-bold text-2xl" onClick={() => setStateFunction(false)}>
                <CloseButton />
            </button>
            <div className="ml-6 mt-20">
                <nav className="">
                    <ul className="">
                        <li className="mb-5 hover:translate-x-3 tracking-wider"><a className="hover:text-gray-200 hover:translate-x-3 transition transform" href="">HOME</a></li>
                        <li className="mb-5 tracking-wider"><a href="">CLOTHES</a></li>
                        <li className="mb-5 tracking-wider"><a href="">ELECTRONICS</a></li>
                        <li className="mb-5 tracking-wider"><a href="">FURNITURE</a></li>
                        <li className="mb-5 tracking-wider"><a href="">SHOES</a></li>
                        <li className="mb-5 tracking-wider"><a href="">MISCELLANEOUS</a></li>
                        <li className="mb-5 tracking-wider"><a href="">OFFERS</a></li>
                        <li className="tracking-wider"><a href="">SALE</a></li>
                    </ul>
                </nav>
            </div>
            <span className="ms-madi-regular tracking-wider text-2xl -rotate-12 absolute bottom-24 left-32">WebWish</span>
        </div>
    )
}

必做检查:确认动画CSS定义正确

确保animate-translateX和animate-translateX0的动画逻辑是反向的,示例:

@keyframes slideIn {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

@keyframes slideOut {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}

.animate-translateX {
  animation: slideIn 1s forwards;
}

.animate-translateX0 {
  animation: slideOut 1s forwards;
}
额外提醒

你现在在Header和Home组件里各定义了一套asideBar状态,会导致两个组件的侧边栏状态不统一。如果是全局侧边栏,建议用React Context或状态管理库(如Zustand、Redux)统一管理状态,避免出现状态混乱。

内容的提问来源于stack exchange,提问作者ezequiel correa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:02:10