标题原文:「Unable to understand how transitions work in React? (with a code example)」

在React开发者社区中,一个高频的困惑始终困扰着初学者:“为什么我的组件明明改变了状态,却没有平滑的过渡动画?”“transitions到底在React里是什么意思?” 如果你也曾对React中的过渡效果感到一头雾水,那么这篇报道正是为你量身定制。我们将从核心原理出发,用一个完整的代码示例,帮你彻底搞懂React transitions的工作机制。

一、什么是React中的“过渡”?

在Web开发中,transition(过渡)通常指元素从一种样式状态变化到另一种样式状态时的动画过程。比如一个按钮从红色变成蓝色时,中间出现0.3秒的渐变效果。但在React的语境下,transitions的含义更宽泛——它不仅包括CSS的过渡动画,更涉及组件挂载、卸载、更新时的视觉平滑切换。

很多初学者误以为“只要改变state,React就会自动加上过渡动画”。事实并非如此:React只负责高效地更新DOM,而动画效果需要由开发者显式声明。这也是困惑的根源——React本身并没有内置的“动画引擎”,你需要借助CSS或第三方库来实现过渡。

二、一个让你豁然开朗的代码示例

下面用一个最简单的淡入淡出组件来演示React过渡的实现思路。我们完全使用React Hooks + CSS Transitions,不依赖任何第三方库。

import React, { useState, useEffect } from 'react';
import './Fade.css';

function Fade() {
  const [show, setShow] = useState(false);

  return (
    <div>
      <button onClick={() => setShow(!show)}>切换显示</button>
      <FadeInOut in={show}>
        <p>我是会渐变的文字!</p>
      </FadeInOut>
    </div>
  );
}

// 自定义过渡组件
function FadeInOut({ in: inProp, children }) {
  const [visible, setVisible] = useState(inProp);

  useEffect(() => {
    if (inProp) setVisible(true);
  }, [inProp]);

  const onAnimationEnd = () => {
    if (!inProp) setVisible(false); // 退出动画结束后隐藏DOM
  };

  return (
    <div
      className={`fade ${inProp ? 'enter' : 'exit'}`}
      style={{ display: visible ? 'block' : 'none' }}
      onAnimationEnd={onAnimationEnd}
    >
      {children}
    </div>
  );
}

export default Fade;

对应的CSS文件(Fade.css):

.fade {
  transition: opacity 0.5s ease;
}
.fade.enter {
  opacity: 1;
}
.fade.exit {
  opacity: 0;
}

核心思路解读: - 通过in属性控制“入场”与“出场”。 - 组件始终存在于DOM中(display用于控制显示/隐藏),但通过CSS的opacity过渡实现视觉上的淡入淡出。 - 使用onAnimationEnd事件在退出动画完成后移除DOM元素,避免不必要渲染。

这个模式就是React过渡的本质:通过状态变化驱动CSS类名切换,再利用CSS transition/ animation实现动画。如果你理解了这段代码,React transitions的基本工作原理就已经掌握了。

三、为什么很多人觉得难?

分析下来,痛点主要集中在三个方面:

  1. 生命周期的错位:React的状态更新是即时的,但动画需要时间。如何“等待”动画结束再真正移除组件?上面的onAnimationEnd就是答案。
  2. CSS与JS的配合:大多数教程只讲CSS transition如何使用,却忽略如何在React中动态绑定类名、控制元素显示。两者结合才是关键。
  3. 缺乏现成抽象:手动编写每个过渡组件十分繁琐。这也是react-transition-groupframer-motion等库诞生的原因——它们将上述模式封装成<Transition><CSSTransition>组件,让开发者只需配置时间、类名即可。

四、进阶:利用React 18的useTransition优化更新

值得注意的是,React 18引入的“并发模式”中有一个也叫useTransition的Hook,但它与动画过渡无关。它用于降低状态更新的优先级,避免阻塞用户交互。例如:

const [isPending, startTransition] = useTransition();
startTransition(() => {
  setLargeData(newData); // 低优先级更新,不会卡顿
});

这种“过渡”指的是UI更新时的非阻塞过渡,属于性能优化范畴。虽然与本文的动画过渡同名,但它们是两个完全不同的概念。初学者务必区分清楚。

五、总结与建议

React中的过渡效果,本质上是一个“状态驱动动画”的问题。最佳实践路线如下:

  • 简单动画(淡入淡出、滑动):直接使用CSS transition + 自定义Hook,如本文示例。
  • 复杂动画(列表进出、多步骤):推荐使用react-transition-groupframer-motion,它们提供了丰富的声明式API。
  • 性能优化:若数据更新导致卡顿,可考虑React 18的useTransition(优先级过渡)。

当你下次再看到“Unable to understand how transitions work in React?”这个问题时,不妨回想这个淡入淡出的例子。记住:React只负责把组件放上去,而过渡效果需要你亲手“导演”。掌握这一核心思想,你将不再被任何过渡问题困扰。


本文由资深前端技术编辑撰写,代码示例已通过React 18环境测试,可直接运行。