2、React Transition Group入门:安装与核心组件介绍
好,咱们开始第二讲。这一章我带你认识 React Transition Group 这个库。说实话,我刚接触 React 动画时,第一反应是「直接用 CSS 不就行了?」后来发现,事情没那么简单。
React Transition Group 不是帮你「做动画」的库。它更像一个调度员——管理组件进入、离开的状态,把时机交给你,动画逻辑你自己写。我个人习惯把它叫做「动画状态机」。
2.1 安装与基本配置
安装很简单,一行命令搞定:
npm install react-transition-group
或者用 yarn:
yarn add react-transition-group
装完之后,你会在 node_modules 里看到四个核心文件:Transition、CSSTransition、SwitchTransition、TransitionGroup。嗯,就是咱们这章要讲的主角们。
2.2 Transition 组件——最底层的状态机
Transition 是其他所有组件的基础。它不关心你用什么 CSS,只负责告诉你:「现在组件处于什么状态」。
它有四个核心状态:
| 状态 | 说明 | 触发时机 |
|---|---|---|
entering |
进入中 | 组件挂载或 in 变为 true 时 |
entered |
已进入 | 进入动画完成后 |
exiting |
退出中 | in 变为 false 时 |
exited |
已退出 | 退出动画完成后 |
看个最简单的例子:
import { Transition } from 'react-transition-group';
function FadeIn({ in: inProp, children }) {
const defaultStyle = {
transition: 'opacity 300ms ease-in-out',
opacity: 0,
};
const transitionStyles = {
entering: { opacity: 1 },
entered: { opacity: 1 },
exiting: { opacity: 0 },
exited: { opacity: 0 },
};
return (
<Transition in={inProp} timeout={300}>
{(state) => (
<div style={{
...defaultStyle,
...transitionStyles[state]
}}>
{children}
</div>
)}
</Transition>
);
}
这里有个坑——in 是关键字,不能直接当 prop 名。所以我用了 in: inProp 这种写法。我曾经因为这个 bug 排查了半小时,后来发现是命名冲突。
timeout 属性必须设置。它告诉 Transition 动画持续多久,组件才知道什么时候切换状态。不设置的话,状态会瞬间跳转,动画效果出不来。
2.3 CSSTransition——更省心的写法
说实话,用 Transition 写样式有点麻烦。每次都要手动映射状态到样式对象。CSSTransition 就是来解决这个问题的——你只需要写 CSS class 名,它自动帮你加上。
import { CSSTransition } from 'react-transition-group';
function MyComponent({ visible }) {
return (
<CSSTransition
in={visible}
timeout={300}
classNames="fade"
unmountOnExit
>
<div>Hello World</div>
</CSSTransition>
);
}
对应的 CSS:
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 300ms;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 300ms;
}
你看,classNames 传了个 "fade",CSSTransition 会自动拼接出 fade-enter、fade-enter-active、fade-exit、fade-exit-active 这些 class。你想想看,这比手动写 style 对象舒服多了吧?
我在项目中遇到过一个问题:unmountOnExit 这个属性经常被忽略。不加它的话,组件退出后 DOM 还在,只是隐藏了。对于列表类组件,这会导致内存泄漏。嗯,记得加上。
2.4 SwitchTransition——两个组件之间的切换动画
有时候你需要做「A 组件消失,B 组件出现」这种切换效果。比如 Tab 切换、路由切换。SwitchTransition 就是干这个的。
import { SwitchTransition, CSSTransition } from 'react-transition-group';
function TabSwitcher({ activeTab }) {
return (
<SwitchTransition mode="out-in">
<CSSTransition
key={activeTab}
timeout={300}
classNames="tab"
>
<div>{activeTab === 'a' ? 'Tab A 内容' : 'Tab B 内容'}</div>
</CSSTransition>
</SwitchTransition>
);
}
这里有个关键点:key 属性。SwitchTransition 通过 key 的变化来判断「哪个组件走了,哪个组件来了」。没有 key 的话,它不知道你在切换。
mode 属性有三种模式:
out-in:旧组件先退出,新组件再进入。我比较常用这种,视觉上更自然。in-out:新组件先进入,旧组件再退出。适合某些特殊场景。- 不设置:同时进行,容易产生重叠混乱。
2.5 TransitionGroup——列表动画的救星
列表动画是最头疼的。新增、删除、排序,每个操作都要考虑动画。TransitionGroup 就是专门解决这个问题的。
import { TransitionGroup, CSSTransition } from 'react-transition-group';
function TodoList({ items }) {
return (
<TransitionGroup className="todo-list">
{items.map(item => (
<CSSTransition
key={item.id}
timeout={500}
classNames="item"
>
<div>{item.text}</div>
</CSSTransition>
))}
</TransitionGroup>
);
}
TransitionGroup 会自动监听子组件的变化。新增时触发 enter 动画,删除时触发 exit 动画。你想想看,如果没有它,你得手动维护每个列表项的动画状态,那代码量就大了去了。
这里有个小技巧:TransitionGroup 本身不处理动画,它只负责管理子组件的挂载和卸载。真正的动画逻辑还是交给 CSSTransition 或 Transition 来做。
timeout。如果 timeout 设置得太短,动画还没做完组件就被移除了,会出现闪烁。我一般设 300-500ms,视觉上比较流畅。
2.6 四个组件的选择建议
说了这么多,你可能有点晕:到底该用哪个?我总结一下:
- 单个组件的进入/退出:用 CSSTransition。90% 的场景都够用。
- 需要精细控制状态:用 Transition。比如你想在 entering 状态做特殊处理。
- 两个组件切换:用 SwitchTransition + CSSTransition。
- 列表动画:用 TransitionGroup + CSSTransition。
说白了,CSSTransition 是主力,其他三个是辅助。我刚开始学的时候,只用 CSSTransition 就解决了大部分需求。后来遇到复杂场景,才慢慢用上其他组件。
好,这一章就到这里。下一章咱们会深入 CSSTransition 的各种配置项,包括动画钩子函数、自定义 class 前缀等等。到时候我会分享一些实际项目中的踩坑经验。