React CSSTransition列表项动画不生效问题求助
解决React Transition Group列表项逐个入场动画不生效的问题
核心问题分析
原代码存在几个关键问题导致动画失效:
- TransitionGroup的嵌套结构错误,无法正确监听子元素的挂载状态
- 使用
createRef存储元素引用的方式不可靠,组件重渲染时会丢失引用 - 错误地将动画延迟加到
timeout属性上,而非设置CSS过渡延迟 - 冗余的key属性和不必要的CSS属性干扰动画逻辑
修复后的完整代码
组件代码
import { useState, useRef, useEffect } from 'react'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; const CharList = (props) => { const [chars, setChars] = useState([]); // 用useRef数组存储每个列表项的引用,保证引用稳定 const itemRefs = useRef([]); const onCharLoaded = async (newChars) => { // 不需要在char对象里存ref,直接用useRef数组管理 setChars(prevChars => [...prevChars, ...newChars]); }; // 发起API请求获取数据的逻辑 useEffect(() => { const fetchChars = async () => { // 替换为实际API请求 const mockChars = [ { id: 1, name: 'Iron Man', thumbnail: 'https://example.com/ironman.jpg' }, { id: 2, name: 'Captain America', thumbnail: 'https://example.com/cap.jpg' }, { id: 3, name: 'Thor', thumbnail: 'https://example.com/thor.jpg' } ]; await onCharLoaded(mockChars); }; fetchChars(); }, []); const focusOnItem = (ref) => { ref.current?.focus(); }; function renderItems(arr) { return ( // TransitionGroup直接作为ul容器,component指定为ul <TransitionGroup component="ul" className="char__grid"> {arr.map((item, i) => ( <CSSTransition key={item.id} // timeout = 动画时长 + 当前项的延迟时间,确保CSSTransition等待动画完成 timeout={300 + i * 100} classNames="item" // 使用回调ref存入useRef数组 nodeRef={itemRefs.current[i]} > <li className="char__item" tabIndex={0} // 回调ref,将元素存入数组 ref={el => itemRefs.current[i] = el} onClick={() => { props.onCharSelected(item.id); focusOnItem(itemRefs.current[i]); }} onKeyDown={(e) => { if (e.key === " " || e.key === "Enter") { props.onCharSelected(item.id); focusOnItem(itemRefs.current[i]); } }} // 给每个项设置过渡延迟,实现逐个出现的效果 style={{ transitionDelay: `${i * 100}ms` }} > <img src={item.thumbnail} alt={item.name} /> <div className="char__name">{item.name}</div> </li> </CSSTransition> ))} </TransitionGroup> ); } return <>{renderItems(chars)}</>; }; export default CharList;
CSS代码
/* 简化动画样式,去掉不必要的visibility属性 */ .item-enter { opacity: 0; transform: translateY(20px); /* 可选:添加位移效果增强动画 */ } .item-enter-active { opacity: 1; transform: translateY(0); transition: all 300ms ease-out; /* 指定过渡曲线,动画更流畅 */ } .item-exit { opacity: 1; transform: translateY(0); } .item-exit-active { opacity: 0; transform: translateY(20px); transition: all 300ms ease-out; } /* 基础列表样式 */ .char__grid { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 1rem; } .char__item { cursor: pointer; padding: 0.5rem; border-radius: 4px; text-align: center; } .char__item img { width: 100%; height: auto; border-radius: 4px; }
关键修复点说明
- TransitionGroup结构调整:将
TransitionGroup设置为列表容器(通过component="ul"),确保它能直接监听子元素CSSTransition的挂载/卸载事件,这是动画生效的前提。 - 引用管理优化:使用
useRef([])数组存储列表项引用,替代原有的createRef,避免组件重渲染时丢失引用,确保nodeRef能正确绑定到DOM元素。 - 动画延迟设置:通过
style={{ transitionDelay:${i * 100}ms}}给每个项设置递增的延迟,而不是修改timeout属性。timeout属性仅用于告知CSSTransition等待动画完成的总时长(动画时长+延迟)。 - CSS简化:移除不必要的
visibility属性,改用opacity和可选的transform实现平滑过渡,避免属性冲突。 - 冗余key移除:仅在
CSSTransition上设置唯一key,不需要在内部li元素重复设置。
内容的提问来源于stack exchange,提问作者Elena
相关产品推荐
相关产品推荐

