使用framer-motion的AnimatePresence时组件提前点击消失问题
问题原因
你的代码核心问题在于整个Component组件在状态切换时被直接卸载替换:
- App组件中,
boolean状态变化时会直接返回全新的Component实例,旧的Component会被立即销毁,其内部的AnimatePresence还没完成exit动画就跟着组件一起消失,导致h1元素直接从DOM中移除。 - 你给
AnimatePresence设置的mode="wait"只对其内部子元素的切换生效,但这里的问题是AnimatePresence本身被卸载了,所以这个配置无法发挥作用。
解决办法
有两种常见的修复思路,核心都是避免Component被直接卸载,或者让AnimatePresence管理组件的完整生命周期:
方案一:将AnimatePresence提升到App层,管理Component的切换
把AnimatePresence放到App组件中,给Component添加唯一key,让AnimatePresence负责旧组件的退出动画和新组件的进入动画:
import { useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; export default function App() { const [boolean, setBoolean] = useState<boolean>(true); return ( <AnimatePresence mode="wait"> <Component key={boolean ? "A" : "B"} text={boolean ? "A" : "B"} onClick={() => setBoolean(!boolean)} /> </AnimatePresence> ); } function Component({ text, onClick }: { text: string; onClick: () => void }) { return ( <> <motion.h1 initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} > Title {text} </motion.h1> <button onClick={onClick}>Button {text}</button> </> ); }
方案二:将状态移到Component内部,避免组件卸载
把控制文本切换的状态放到Component组件内部,这样Component始终不会被卸载,只在内部通过key切换h1元素,让AnimatePresence管理h1的进出动画:
import { useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; export default function App() { return <Component />; } function Component() { const [text, setText] = useState<string>("A"); return ( <> <AnimatePresence mode="wait"> <motion.h1 key={text} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} > Title {text} </motion.h1> </AnimatePresence> <button onClick={() => setText(text === "A" ? "B" : "A")}> Button {text} </button> </> ); }
效果说明
两种方案都能解决中途点击导致组件消失的问题:
- 方案一中,
AnimatePresence会等待旧Component的exit动画完成后,再挂载新的Component,确保动画过程中始终有元素存在。 - 方案二中,
Component保持挂载状态,AnimatePresence只处理h1元素的切换动画,即使中途点击,也只会触发新的动画队列,不会出现元素消失的情况。
内容的提问来源于stack exchange,提问作者Martí Serra Molina
相关产品推荐
相关产品推荐

