Framer Motion AnimatePresence退出动画重复渲染问题
NextJs + Framer Motion 卡片动画重复渲染问题解决
问题场景
使用NextJs、Framer Motion和Tailwind开发时,页面中的卡片点击后会动画到屏幕中间,再次点击返回原列表。但再次点击同一卡片时,退出动画会出现Item组件重复渲染的问题。移除AnimatePresence组件后问题消失,但需要保留退出动画,因此需要解决该问题。
相关代码
List 组件
export function List({ selectedId, setSelectedId, }: { selectedId: string | null; setSelectedId: any; }) { return ( <React.Fragment> <div className="w-full max-w-5xl flex-[1_1_100%] py-11 px-6"> <ul className="flex flex-wrap content-start"> {items.map((card) => ( <Card key={card.id} {...card} selectedId={selectedId} setSelectedId={setSelectedId} /> ))} </ul> </div> <AnimatePresence> {selectedId && ( <Item id={selectedId} key="item" setSelectedId={setSelectedId} /> )} </AnimatePresence> </React.Fragment> ); }
Item 组件
export function Item({ id, setSelectedId, }: { id: string; setSelectedId: any; }) { const item = items.find((item) => item.id === id) as Item; const { title, category, image } = item; return ( <React.Fragment> <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0, transition: { duration: 0.15 } }} transition={{ duration: 0.2, delay: 0.15 }} style={{ pointerEvents: "auto" }} className="overlay z-[1]" > <div onClick={() => setSelectedId(null)}></div> </motion.div> <motion.div className="card-content-container open" transition={{ duration: 0.5 }} > <motion.div className="card-content z-[1001]" layout layoutId={`card-container-${id}`} > <motion.div className="card-image-container" layoutId={`card-image-container-${id}`} > <Image className="card-image" src={image.src} alt="" fill priority onClick={() => setSelectedId(null)} /> </motion.div> <motion.div className="title-container" layoutId={`title-container-${id}`} > <span className="category">{category}</span> <h2>{title}</h2> </motion.div> <motion.div className="content-container" layoutId={`content-container-${id}`} > <LoremIpsum p={1} avgWordsPerSentence={5} avgSentencesPerParagraph={2} /> </motion.div> </motion.div> </motion.div> </React.Fragment> ); }
问题原因与解决办法
问题根源
问题出在Item组件的key值固定为"item"。Framer Motion的AnimatePresence组件依赖key来追踪组件的挂载和卸载状态:
- 第一次点击卡片,selectedId从null变为对应ID,Item组件挂载;
- 再次点击同一卡片,selectedId变为null,Item组件卸载;
- 第三次点击同一卡片,selectedId变回该ID,此时因为key还是"item",React会认为是同一个组件进行更新而非重新挂载,导致AnimatePresence的退出动画逻辑混乱,进而出现重复渲染。
解决步骤
修改List组件中Item的key,将固定的"item"替换为selectedId:
<AnimatePresence> {selectedId && ( <Item id={selectedId} key={selectedId} // 绑定key为选中的ID setSelectedId={setSelectedId} /> )} </AnimatePresence>
这样每次切换不同卡片,或同一卡片反复点击时,AnimatePresence都能正确识别组件的挂载/卸载状态,退出动画就能正常执行,不会出现重复渲染的问题。
对比你参考的正常运行的沙箱示例,它的弹窗组件key也是与选中ID绑定的,这就是它能正常工作的核心原因。
内容的提问来源于stack exchange,提问作者cheezit97
相关产品推荐
相关产品推荐

