You need to enable JavaScript to run this app. Next.js监听路由变更时如何修改状态实现菜单开合动画
优惠活动
大模型
产品
解决方案
定价
更多

Next.js监听路由变更时如何修改状态实现菜单开合动画

问题定位与修复方案

问题定位

  • 原代码里的useEffect未配置依赖数组,属于每次组件重渲染都会执行的副作用。长此以往会反复绑定routeChangeStart事件,导致事件监听器叠加,路由触发时会多次执行setMenuState,最终状态值因频繁覆盖出现更新异常。
  • 逻辑耦合混乱:把路由事件绑定、状态更新、菜单动画三类完全独立的逻辑塞在同一个副作用里,执行时序不可控。
  • 冗余代码:已经用useRouter拿到了路由实例,根本没必要再用withRouter高阶组件包裹一层,平白增加不必要的组件重渲染。

修复后的可运行代码

import Link from 'next/link'
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { UpArrowCircle } from '@/components/SvgFiles'
import { openMenu, closeMenu } from '@/components/MenuAnimations'

const Header = ({ dimensions }) => {
  const [menuOpened, setMenuOpened] = useState(false)
  const router = useRouter()

  // 路由切换时自动关闭菜单,组件挂载时仅绑定一次事件
  useEffect(() => {
    const handleRouteChange = () => {
      setMenuOpened(false)
    }
    // 服务端渲染阶段无window对象,加判断避免运行报错
    if (typeof window !== 'undefined') {
      router.events.on('routeChangeStart', handleRouteChange)
    }
    // 组件卸载时清理事件监听,避免内存泄漏
    return () => {
      if (typeof window !== 'undefined') {
        router.events.off('routeChangeStart', handleRouteChange)
      }
    }
  }, [router.events])

  // 监听菜单状态变化,触发对应的开合动画
  useEffect(() => {
    if (menuOpened) {
      openMenu(dimensions.width)
    } else {
      closeMenu()
    }
  }, [menuOpened, dimensions.width])

  return (
    <header className="header">
      <div className="container">
        <div className="row v-center space-between">
          <div className="logo">
            <Link href="/">
              <a>AGENCY</a>
            </Link>
          </div>
          <div className="nav-toggle">
            <div
              onClick={() => setMenuOpened(true)}
              className="hamburger-menu"
            >
              <span></span>
              <span></span>
            </div>
            <div
              className="hamburger-menu-close"
              onClick={() => setMenuOpened(false)}
            >
              <UpArrowCircle />
            </div>
          </div>
        </div>
      </div>
    </header>
  )
}
export default Header

关键优化说明

  • 状态结构简化:把原来嵌套在对象里的menuOpened拆为独立的布尔值状态,不用每次更新都新建对象引用,减少不必要的重渲染开销。
  • 副作用拆分:把原来混在一起的逻辑拆成两个独立的副作用,一个负责路由事件绑定,一个负责动画触发,每个都配置正确的依赖数组,保证逻辑在正确的时机执行,不会无效重复运行。
  • 兼容处理:增加客户端环境判断,不会在Next.js服务端渲染阶段因为找不到浏览器专属对象出现运行崩溃。

内容的提问来源于stack exchange,提问作者Clovis Rosa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 02:06:05