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。嗯,就是咱们这章要讲的主角们。

小提示: 我建议你安装时顺便看一眼 package.json 里的版本号。不同版本 API 略有差异,咱们课程基于 v4.x 版本讲解。

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-enterfade-enter-activefade-exitfade-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:新组件先进入,旧组件再退出。适合某些特殊场景。
  • 不设置:同时进行,容易产生重叠混乱。
避坑指南: 我曾经在 SwitchTransition 里忘了给子组件加 key,结果动画完全没生效。调试了半天才发现。记住:SwitchTransition 的子组件必须有唯一的 key。

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 来做。

个人经验: 使用 TransitionGroup 时,记得给每个子组件设置 timeout。如果 timeout 设置得太短,动画还没做完组件就被移除了,会出现闪烁。我一般设 300-500ms,视觉上比较流畅。

2.6 四个组件的选择建议

说了这么多,你可能有点晕:到底该用哪个?我总结一下:

  • 单个组件的进入/退出:用 CSSTransition。90% 的场景都够用。
  • 需要精细控制状态:用 Transition。比如你想在 entering 状态做特殊处理。
  • 两个组件切换:用 SwitchTransition + CSSTransition。
  • 列表动画:用 TransitionGroup + CSSTransition。

说白了,CSSTransition 是主力,其他三个是辅助。我刚开始学的时候,只用 CSSTransition 就解决了大部分需求。后来遇到复杂场景,才慢慢用上其他组件。

好,这一章就到这里。下一章咱们会深入 CSSTransition 的各种配置项,包括动画钩子函数、自定义 class 前缀等等。到时候我会分享一些实际项目中的踩坑经验。