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

如何阻止React函数组件重复渲染并避免Framer Motion动画重复播放

解决React+Framer Motion状态变更时动画重复播放的问题

问题根源分析

你的代码里有几个关键问题导致动画重复播放:

  1. IconList组件定义在App内部:每次App组件因stateChange更新重渲染时,IconList都会被重新创建为全新的组件类型,React.memo完全起不到缓存作用。
  2. React.memo依赖参数错误:你把外部状态isViewFirstLoad作为memo的依赖,但memo的第二个参数应该用来比较组件接收的props是否变化,而非外部状态。
  3. 索引作为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:12:38