You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需状态/Props实现react-transition-group Modal淡入淡出过渡

实现无Props/状态控制的Modal淡入淡出动画

你的核心需求是让Modal仅通过父组件是否渲染它来自动触发淡入/淡出,不需要传递任何控制状态或Props,这个需求其实可以通过调整react-transition-group的使用方式来实现,问题出在你当前把TransitionGroup放在了Modal组件内部——这会导致父组件移除Modal时,整个组件(包括内部的TransitionGroup)被立即卸载,根本没机会播放退出动画。

正确的实现思路

TransitionGroup的作用是监听它直接包裹的子元素的添加/移除,并自动为子元素内的CSSTransition触发进入/退出动画。所以我们需要把TransitionGroup移到父组件中,用来包裹Modal的条件渲染,而Modal组件只负责封装Portal和动画逻辑,不需要接收任何控制类Props。

完整代码示例

1. 父组件(负责控制Modal的渲染与否)

import { useState } from 'react';
import { TransitionGroup } from 'react-transition-group';
import Modal from './Modal';

function App() {
  // 这个状态只在父组件内部使用,不需要传递给Modal
  const [showModal, setShowModal] = useState(true);

  return (
    <div className="app">
      <button onClick={() => setShowModal(!showModal)}>
        切换Modal显示
      </button>
      {/* TransitionGroup必须直接包裹动态渲染的Modal */}
      <TransitionGroup>
        {/* 给Modal设置唯一key,确保TransitionGroup能识别它的添加/移除 */}
        {showModal && <Modal key="modal">这是Modal内容</Modal>}
      </TransitionGroup>
    </div>
  );
}

2. Modal组件(封装Portal和动画,无控制Props)

import Fade from './Fade';
import Portal from './Portal'; // 假设你已经实现了Portal组件

const Modal = ({ children, ...other }) => {
  return (
    <Portal>
      {/* 直接用Fade包裹内容,不需要内部的TransitionGroup */}
      <Fade>
        <div className="modal-container" {...other}>
          {children}
        </div>
      </Fade>
    </Portal>
  );
};

export default Modal;

3. Fade组件(复用CSSTransition逻辑)

import { CSSTransition } from 'react-transition-group';

const Fade = ({ children, ...other }) => (
  <CSSTransition
    {...other}
    timeout={500}
    classNames="fade"
    unmountOnExit // 动画结束后自动卸载DOM
  >
    {children}
  </CSSTransition>
);

export default Fade;

4. 对应的CSS样式

.modal-container {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 2rem;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}

/* 淡入动画类 */
.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

/* 淡出动画类 */
.fade-exit {
  opacity: 1;
}
.fade-exit-active {
  opacity: 0;
  transition: opacity 500ms ease-out;
}

为什么这样可行?

  • 当父组件渲染<Modal />时,TransitionGroup会检测到新子元素的添加,自动触发CSSTransition的进入动画,实现淡入效果。
  • 当父组件停止渲染<Modal />时,TransitionGroup不会立即移除Modal的DOM,而是触发CSSTransition的退出动画,等动画完成后才会真正卸载Modal,实现淡出效果。
  • Modal组件本身不需要接收任何控制状态或Props,完全符合你的需求——父组件只需要决定是否渲染它即可。

关于旧版react-css-transition-group的差异

你提到旧版方案可行,是因为旧版的TransitionGroup会自动监听子组件的挂载/卸载生命周期,无需显式在父层包裹。而新版react-transition-group调整了设计,要求TransitionGroup必须直接包裹动态变化的子元素,才能正确捕获添加/移除事件,这就是你之前实现失效的原因。

内容的提问来源于stack exchange,提问作者Myles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:19:59