React Transition Group CSS过渡异常问题排查与修复求助
组件切换CSS过渡问题排查与修复
问题描述
使用React Transition Group的SwitchTransition和CSSTransition实现组件切换时遇到三个问题:
- 过渡方向不符:实际为从右到左,预期为从左到右
- 过渡时机错误:旧组件直接被替换后新组件才开始过渡,预期新旧组件同时进行过渡
- 交换
.fadeEnterActive与.fadeExitActive类后,第二个问题解决,但进入元素初始就带有.fadeEnterDone类名
现有代码
React组件代码
import { useRef, useState } from "react"; import { SwitchTransition, CSSTransition } from "react-transition-group"; import PayContainer from "../payContainer"; import PersonalInfoForm from "../persnalInfoForm"; import styles from './drawer_container.module.scss' export default function DrawerContainer() { const [state, setState] = useState('pay-fill'); const payRef = useRef(null); const personalInfoRef = useRef(null); const previewRef = useRef(null); const nodeRef = () => { switch (state) { case 'pay-fill': return payRef case 'personalinfo-fill': return personalInfoRef case 'preview': return previewRef default: return null; } } const setActiveState = (curState: string) => { setState(curState) } const getWidget = () => { switch (state) { case 'pay-fill': return <PayContainer setState={setActiveState}/> case 'personalinfo-fill': return <PersonalInfoForm /> case 'preview': return <div>preview nigga</div> default: return <div>default</div>; } } return <div className="w-full h-full"> <SwitchTransition mode={'out-in'}> <CSSTransition key={state} nodeRef={nodeRef()} addEndListener={(done: () => void) => { nodeRef()?.current?.addEventListener("transitionend", done, false); }} timeout={500} classNames={{ enterActive: styles.fadeEnterActive, enterDone: styles.fadeEnterDone, exitActive: styles.fadeExitActive, exitDone: styles.fadeExitDone }} > <div ref={nodeRef()} className={['w-full h-full', styles.fade].join(' ')}> {getWidget()} </div> </CSSTransition> </SwitchTransition> </div>; }
SCSS样式代码
.fadeEnterActive { opacity: 0; transform: translateX(-100%); } .fadeEnterDone { opacity: 1; transform: translateX(0%); } .fadeExitActive { opacity: 1; transform: translateX(0%); } .fadeExitDone { opacity: 0; transform: translateX(100%); } .fadeEnterActive, .fadeExitActive { transition: opacity 500ms, transform 500ms; } .fade { }
问题核心分析
- 多Ref绑定错误:为每个状态单独创建ref,导致
CSSTransition无法跟踪旧元素的退出状态,直接卸载旧元素后才渲染新元素,引发过渡时机错误。 - CSS类逻辑缺失:未定义
enter和exit初始状态类,仅用enterActive作为进入起始状态,导致过渡方向逻辑混乱,且无法正确触发状态切换。 - 切换模式不匹配:
SwitchTransition的out-in模式是旧元素完全退出后新元素才进入,与“同时过渡”的需求不符。
完整修复方案
修复后的React组件代码
import { useRef, useState } from "react"; import { SwitchTransition, CSSTransition } from "react-transition-group"; import PayContainer from "../payContainer"; import PersonalInfoForm from "../persnalInfoForm"; import styles from './drawer_container.module.scss' export default function DrawerContainer() { const [state, setState] = useState('pay-fill'); // 统一使用单个ref绑定切换容器 const containerRef = useRef(null); const setActiveState = (curState: string) => { setState(curState) } const getWidget = () => { switch (state) { case 'pay-fill': return <PayContainer setState={setActiveState}/> case 'personalinfo-fill': return <PersonalInfoForm /> case 'preview': return <div>preview nigga</div> default: return <div>default</div>; } } return ( <div className={`w-full h-full relative ${styles.switchContainer}`}> <SwitchTransition mode="in-out"> <CSSTransition key={state} nodeRef={containerRef} timeout={500} classNames={{ enter: styles.fadeEnter, enterActive: styles.fadeEnterActive, enterDone: styles.fadeEnterDone, exit: styles.fadeExit, exitActive: styles.fadeExitActive, exitDone: styles.fadeExitDone }} > <div ref={containerRef} className={`w-full h-full absolute top-0 left-0 ${styles.fade}`}> {getWidget()} </div> </CSSTransition> </SwitchTransition> </div> ); }
修复后的SCSS样式代码
.switchContainer { overflow: hidden; // 隐藏容器外的过渡元素 } .fade { position: absolute; top: 0; left: 0; } // 进入动画初始状态:从左侧容器外进入 .fadeEnter { opacity: 0; transform: translateX(-100%); } .fadeEnterActive { opacity: 1; transform: translateX(0); transition: opacity 500ms, transform 500ms ease-in-out; } .fadeEnterDone { opacity: 1; transform: translateX(0); } // 退出动画初始状态:停留在当前位置 .fadeExit { opacity: 1; transform: translateX(0); } .fadeExitActive { opacity: 0; transform: translateX(100%); // 向右移出容器 transition: opacity 500ms, transform 500ms ease-in-out; } .fadeExitDone { opacity: 0; transform: translateX(100%); }
修复说明
- 统一Ref绑定:移除多ref设置,改用单个
containerRef绑定切换容器,确保CSSTransition能正确监听元素的进入/退出状态,解决旧组件直接被替换、新元素初始带enterDone类的问题。 - 修正CSS过渡逻辑:补充
fadeEnter和fadeExit初始状态类,明确过渡的起始/结束状态,将进入动画初始位置设为translateX(-100%)(左侧外),退出动画结束位置设为translateX(100%)(右侧外),完美匹配从左到右的预期过渡方向。 - 布局优化:容器设置
relative,切换元素设置absolute实现新旧元素重叠,配合容器overflow: hidden避免过渡元素超出可视范围,达到同时过渡的视觉效果。 - 切换模式调整:
SwitchTransition使用mode="in-out",让新旧组件同时触发过渡动画;若需要先退出再进入的效果,可改回mode="out-in"。
内容的提问来源于stack exchange,提问作者bihire boris
相关产品推荐
相关产品推荐

