为什么Framer Motion的动画在组件卸载时会报错?

梓奥 ☘︎ 阅读 17

我在用Framer Motion的useAnimation控制侧边栏动画时,页面跳转后控制台总是报错:Cannot read properties of null (reading 'style')。代码里明明加了清理函数,但问题还是存在。

具体场景是点击关闭按钮后,侧边栏会执行收起动画,动画结束后跳转页面。但有时候跳转速度比动画快,就会报这个错。尝试过在useEffect里加依赖项和手动清除动画队列都没用。


import { useAnimation, motion } from 'framer-motion';

function Sidebar() {
  const controls = useAnimation();
  const close = () => {
    controls.start({ x: '-100%' });
    controls.onPlay(() => {
      setTimeout(() => window.location.href = '/', 500);
    });
  };

  return (
    <motion.div animate={controls}>
      <button onClick={close}>关闭</button>
    </motion.div>
  );
}

错误发生在跳转时,感觉是动画还在执行就被打断了。是不是应该在路由跳转前先等待动画完成?或者需要给动画加个ref来手动清理?

我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
Zz胜捷
Zz胜捷 Lv1
这个问题我之前也遇到过,确实挺烦人的。核心问题出在组件卸载后动画还在执行,而Framer Motion的动画控制器没有感知到组件已经被销毁了,所以尝试操作一个不存在的DOM节点。

解决办法其实不复杂,你可以用一个标志位来确保动画只在组件挂载时执行。具体来说就是在useEffect里监听组件的挂载状态,然后在动画执行前检查这个状态。

下面是修改后的代码:


import { useAnimation, motion } from 'framer-motion';
import { useEffect, useState } from 'react';

function Sidebar() {
const controls = useAnimation();
const [isMounted, setIsMounted] = useState(true);

useEffect(() => {
return () => setIsMounted(false); // 组件卸载时更新状态
}, []);

const close = () => {
if (!isMounted) return; // 确保组件已挂载

controls.start({ x: '-100%' }).then(() => {
if (isMounted) {
window.location.href = '/';
}
});
};

return (
<motion.div animate={controls}>
<button onClick={close}>关闭</button>
</motion.div>
);
}


这里的关键点有两个:第一是用isMounted来跟踪组件的挂载状态,防止在组件卸载后还执行跳转逻辑;第二是在动画的then回调里再次检查挂载状态,确保跳转只会在组件还存在时发生。

当时我也试过其他方法,比如手动调用controls.stop()来中断动画,但发现这种方式不够可靠。后来改成这种挂载状态的方案才彻底解决问题。

对了,如果你用的是React Router,建议换成useHistoryuseNavigate来做页面跳转,会比直接改window.location更优雅一些。
点赞 2
2026-02-17 09:29