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

Next.js搭配Framer Motion实现页面转场按返回键报错如何解决?

错误产生原因
  • 路由事件监听未做副作用管理:直接在MyApp函数体中注册Router事件监听,没有包裹在useEffect钩子中,也没有对应的清理逻辑,会导致每次App组件重渲染都重复注册事件,浏览器回退触发路由变化时会多次执行NProgress的回调,和Framer Motion的动画执行时序产生冲突,触发渲染异常。
  • 页面组件的唯一键取值不合理:用router.route作为Component的key,该值是路由模板字符串(动态路由下所有参数不同的同模板路由key都相同),无法保证所有路由场景下的key唯一性,会导致AnimatePresence无法正确识别页面切换,触发组件卸载动画执行时的状态异常。
  • 动画节点的层级冲突:在App层用AnimatePresence包裹页面组件,同时页面内部Layout的motion.div又声明了exit动画属性,两层的动画时序没有对齐,回退场景下容易出现动画执行时组件已经被卸载的情况,触发React错误边界的回退。
可行修复方案
  • 修复路由事件的副作用管理:把Router事件的注册逻辑移到useEffect中,同时返回清理函数卸载事件监听器,修改后的_app.js相关代码如下:
import { useEffect } from 'react'

function MyApp({ Component, pageProps, router }) {
  useEffect(() => {
    NProgress.configure({
      minimum: 0.3,
      easing: 'ease',
      speed: 800,
      showSpinner: true,
    });
    const handleStart = () => NProgress.start();
    const handleEnd = () => NProgress.done();
    Router.events.on('routeChangeStart', handleStart);
    Router.events.on('routeChangeComplete', handleEnd);
    Router.events.on('routeChangeError', handleEnd);
    // 组件卸载时清理事件监听
    return () => {
      Router.events.off('routeChangeStart', handleStart);
      Router.events.off('routeChangeComplete', handleEnd);
      Router.events.off('routeChangeError', handleEnd);
    }
  }, [])

  return (
    <>
      <AnimatePresence exitBeforeEnter>
        <Component {...pageProps} key={router.asPath} />
      </AnimatePresence>

      <ToastContainer
        progressClassName="toastProgress"
        bodyClassName="toastBody"
        position="top-right"
        autoClose={5000}
        hideProgressBar={false}
        newestOnTop={false}
        draggable={false}
        pauseOnVisibilityChange
        closeOnClick
        pauseOnHover
      />
    </>
  );
}
  • 修改页面组件的key取值:把原有的router.route替换为router.asPath,这个值是完整的实际路由路径(包含动态参数),可以保证不同路由页面的key唯一,正确触发AnimatePresence的动画流程。
  • 统一动画层级:要么把motion动画的声明全部放到App层包裹Component的节点上,要么把AnimatePresence移到Layout组件内部,避免两层动画逻辑冲突。如果选择保留Layout内的动画逻辑,可以去掉App层的AnimatePresence包裹,统一在Layout内管理进入退出动画即可。
  • 可选优化:如果调整后仍有偶发报错,可以给AnimatePresence加上mode="wait"属性,确保前一个页面完全卸载、动画执行完成后再渲染新页面,避免状态冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 16:06:04