React前置添加用户时CSS首元素动画仅首次生效的问题
解决前置添加用户时首项CSS动画仅触发一次的问题
问题描述
每次在列表前置添加新用户时,希望第一个<div>触发CSS淡入缩放动画,但当前代码仅第一次添加时动画生效,后续添加无效果。
原代码
JS代码
const [userList, setUserList] = useState<any>([]); <div className='user-listing absolute top-0 left-0'> { userList.map((user:any, index:number)=>{ return ( <div key={index} className='user'>{user}</div> ) }) } </div> <button onClick={()=> setUserList((list:any) => [randomUser(),...list])}> add </button>
SCSS代码
.user-listing{ .user{ &:first-child { animation-name: fadeIn; animation-duration: .15s; } } } @keyframes fadeIn { 0% { opacity: 0; transform: scale(.2) } to { opacity: 1; transform: scale(1) } }
问题原因
- 使用index作为key的缺陷:前置添加元素时,React会复用原有DOM节点(因为index重新排序后,新元素的key=0对应之前第一个元素的DOM),导致新元素并非全新渲染,不会触发
:first-child的初始动画。 - CSS动画的触发机制:动画仅在元素首次应用样式时触发,复用DOM节点时元素已处于动画结束状态,不会重复执行。
解决方案
1. 改用唯一标识作为key
不要用index,给每个用户项分配唯一ID,确保新添加的用户会生成全新DOM节点。
2. 通过动态类名触发动画
利用状态记录需要动画的用户,添加动画类并在动画结束后移除,确保每次添加都能触发动画。
修改后的代码
JS代码(假设randomUser()返回带唯一ID的对象,若仅返回字符串可自行生成ID)
import { useState, useRef } from 'react'; const [userList, setUserList] = useState([]); const animateUserId = useRef(null); const addUser = () => { // 生成带唯一ID的用户(如果randomUser返回字符串,改为{ id: Date.now() + Math.random(), name: randomUser() }) const newUser = randomUser(); animateUserId.current = newUser.id; setUserList(prev => [newUser, ...prev]); }; return ( <> <div className='user-listing absolute top-0 left-0'> {userList.map((user) => { const needAnimate = user.id === animateUserId.current; return ( <div key={user.id} className={`user ${needAnimate ? 'fade-in' : ''}`} onAnimationEnd={() => { if (needAnimate) animateUserId.current = null; }} > {user.name || user} </div> ); })} </div> <button onClick={addUser}>add</button> </> );
SCSS代码
.user-listing { .user { // 保留原有基础样式 } .fade-in { animation: fadeIn .15s forwards; } } @keyframes fadeIn { 0% { opacity: 0; transform: scale(.2); } to { opacity: 1; transform: scale(1); } }
说明
- 用用户唯一ID作为
key,确保新前置的用户会渲染全新DOM元素。 - 通过
animateUserId记录当前需要动画的用户ID,动态添加fade-in类触发动画。 - 监听
animationEnd事件,动画结束后清除标识,避免后续渲染重复触发。
内容的提问来源于stack exchange,提问作者FeelRightz
相关产品推荐
相关产品推荐

