如何阻止React函数组件重复渲染并避免Framer Motion动画重复播放
解决React+Framer Motion状态变更时动画重复播放的问题
问题根源分析
你的代码里有几个关键问题导致动画重复播放:
- IconList组件定义在App内部:每次App组件因
stateChange更新重渲染时,IconList都会被重新创建为全新的组件类型,React.memo完全起不到缓存作用。 - React.memo依赖参数错误:你把外部状态
isViewFirstLoad作为memo的依赖,但memo的第二个参数应该用来比较组件接收的props是否变化,而非外部状态。 - 索引作为key:虽然不是直接触发动画重复的原因,但用索引当key会导致React在列表更新时无法正确识别元素,容易引发不必要的重渲染。
修复方案
直接修改代码如下,关键改动已标注:
import { motion } from 'framer-motion'; import React, { useState, useRef, useEffect } from 'react'; import './App.css'; import { jsonData } from './data'; // 1. 把IconList移到App外部,避免每次App重渲染都重新定义组件 const IconList = React.memo(({ data }) => { // 用useRef记录是否已经完成第一次动画 const hasAnimated = useRef(false); useEffect(() => { hasAnimated.current = true; }, []); return ( data.map((oItem) => { return ( <motion.div // 2. 用唯一id作为key,替代索引 key={oItem.id} // 3. 只在第一次挂载时应用initial状态,之后保持当前样式 initial={hasAnimated.current ? false : { opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 1, ease: "easeOut" }} data-id={oItem.id} className='item-container'> <img src={oItem.thumbnail} /> <div className='content'> <p>{oItem.title}</p> <p>{oItem.description}</p> </div> </motion.div> ) }) ) // 4. 让React.memo默认比较props,因为data是静态的jsonData,只要引用不变就不会重渲染 }); function App() { const [stateChange, setStateChange] = useState(false); return ( <div className="app"> <div className='fixed-header'> <button onClick={() => { setStateChange(!stateChange) }}>Toggle State</button> <p> CHANGE STATUS: <span style={{ fontWeight: 'bold' }}>{stateChange.toString()}</span> </p> </div> <div className='fluid-container'> <IconList data={jsonData} /> </div> </div> ); } export default App;
额外优化点
- 如果你的
jsonData是动态数据(比如从接口获取),可以在App组件里用useMemo缓存jsonData,确保传给IconList的props引用不变:const cachedData = useMemo(() => jsonData, []); // 然后传给IconList <IconList data={cachedData} />
内容的提问来源于stack exchange,提问作者MLElyakan
相关产品推荐
相关产品推荐

