React移动端菜单指定span过渡结束后关闭功能异常求解
问题根源
你现有代码存在3个核心问题,才会导致二次打开自动关闭:
- 直接在组件渲染阶段执行DOM查询、事件绑定,每次组件重渲染都会新增一个
transitionend监听,且从未做事件解绑,残留的监听回调会在后续动画触发时意外执行 - 没有区分「菜单打开阶段的过渡」和「菜单关闭阶段的过渡」,只要
.end元素触发过渡结束就直接翻转isActive状态,打开菜单时的入场动画结束后也会触发状态翻转,直接把刚打开的菜单关掉 - 在React组件中直接使用
document.querySelectorAll操作DOM、手动绑定原生事件,违反React声明式编程逻辑,极易出现状态不同步、内存泄漏问题
实现方案
不需要使用useTransition,仅用useRef+useEffect管理DOM引用和事件监听,加一个标记位区分开/关阶段的动画即可,实现代码如下:
import { useState, useRef, useEffect } from 'react'; // 组件内逻辑 const [isActive, setIsActive] = useState(false); // 绑定最后一个span的DOM实例 const lastSpanRef = useRef(null); // 标记当前是否在等待关闭动画结束 const waitCloseAnim = useRef(false); const toggleNavBar = () => { if (isActive) { // 触发关闭时先打标记,等动画结束再真正修改状态 waitCloseAnim.current = true; } else { // 打开菜单直接切换状态,重置标记 setIsActive(true); waitCloseAnim.current = false; } }; useEffect(() => { const handleTransitionEnd = () => { // 仅在等待关闭的状态下,动画结束才执行关闭 if (waitCloseAnim.current) { setIsActive(false); waitCloseAnim.current = false; } }; const target = lastSpanRef.current; if (target) { target.addEventListener('transitionend', handleTransitionEnd); } // 组件卸载时移除监听,避免内存泄漏 return () => { if (target) target.removeEventListener('transitionend', handleTransitionEnd); }; }, []);
对应的JSX部分只需要给最后一个span补充ref绑定即可,其他结构不变:
<nav className={isActive ? Style.navBarMobile : Style.navBar}> <ul className={Style.navBarList}> <li className={Style.navBarListItem}> <NavLink className={Style.navBarListLink} to='/a_propos'> <div className={Style.original}> <span>à</span> <span> </span> <span>p</span> <span>r</span> <span>o</span> <span>p</span> <span>o</span> <span>s</span> </div> <div className={Style.hoverClone}> <span>à</span> <span> </span> <span>p</span> <span>r</span> <span>o</span> <span>p</span> <span>o</span> <span ref={lastSpanRef} className='end'>s</span> </div> </NavLink> </li> {/* 菜单开关按钮直接绑定toggleNavBar即可 */} </ul> </nav>
逻辑说明
- 用
useRef直接获取目标DOM节点,替代原生DOM查询方法,符合React的DOM管理规则 waitCloseAnim标记位只在用户主动触发关闭时置为true,打开菜单过程中触发的transitionend事件不会执行关闭逻辑- 事件监听仅在组件挂载时绑定一次,卸载时自动移除,不会出现重复绑定、回调意外触发的问题
内容的提问来源于stack exchange,提问作者Alex.f
相关产品推荐
相关产品推荐

