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

使用Hooks实现React组件退出动画(替代componentWillUnmount)

解决组件卸载前淡出动画不生效的问题

这个问题我之前踩过坑——useEffect的清理函数确实是在组件即将被卸载的最后一刻才执行,这时候DOM元素已经进入了移除流程,浏览器根本来不及渲染你的淡出动画。核心思路是不要让组件直接卸载,而是拆分“触发动画”和“实际卸载”两个步骤,具体实现方案如下:

核心方案:用状态控制动画与卸载时机

我们需要拆分两个阶段的状态逻辑:

  1. 标记组件“准备卸载”,触发淡出动画
  2. 监听动画结束事件,完成后真正移除组件

完整代码示例

组件实现(函数式)

import { useState, useRef, useEffect } from 'react';

function FadingCard({ isVisible, onAnimationComplete }) {
  const [isFadingOut, setIsFadingOut] = useState(false);
  const cardRef = useRef(null);

  // 监听父组件的显示/隐藏指令
  useEffect(() => {
    if (!isVisible && !isFadingOut) {
      // 第一步:触发淡出动画
      setIsFadingOut(true);
    }
  }, [isVisible]);

  // 监听动画结束,完成后通知父组件可以卸载自己
  useEffect(() => {
    const handleAnimationEnd = () => {
      // 通知父组件更新状态,移除当前组件
      onAnimationComplete();
      // 重置淡出状态(可选,方便后续重新显示)
      setIsFadingOut(false);
    };

    const element = cardRef.current;
    if (isFadingOut && element) {
      element.addEventListener('transitionend', handleAnimationEnd);
    }

    // 清理监听,避免内存泄漏
    return () => {
      if (element) {
        element.removeEventListener('transitionend', handleAnimationEnd);
      }
    };
  }, [isFadingOut, onAnimationComplete]);

  // 如果既不需要显示,也完成了淡出,直接不渲染
  if (!isVisible && !isFadingOut) {
    return null;
  }

  return (
    <div
      ref={cardRef}
      className={`fading-card ${isFadingOut ? 'fade-out' : ''}`}
    >
      我会优雅地淡出消失 🎇
    </div>
  );
}

// 父组件使用示例
function ParentComponent() {
  const [showCard, setShowCard] = useState(true);

  const handleCardFadeComplete = () => {
    setShowCard(false);
  };

  return (
    <div>
      <button onClick={() => setShowCard(false)}>移除卡片</button>
      {showCard && (
        <FadingCard 
          isVisible={showCard} 
          onAnimationComplete={handleCardFadeComplete} 
        />
      )}
    </div>
  );
}

对应的CSS样式

.fading-card {
  padding: 20px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  /* 定义过渡效果 */
  opacity: 1;
  transition: opacity 0.6s ease-out, transform 0.6s ease-out;
  transform: translateY(0);
}

.fade-out {
  opacity: 0;
  transform: translateY(20px);
}

关键注意点

  • 不要依赖useEffect清理函数做动画:清理函数执行时机太晚,DOM已经处于卸载队列中,动画无法被渲染。
  • 必须监听动画/过渡结束事件:不能用setTimeout硬编码延迟,因为不同浏览器的动画执行时长可能有差异,监听事件是最可靠的方式。
  • 清理事件监听:一定要在useEffect的返回函数中移除监听,避免内存泄漏。
  • 路由场景适配:如果是路由切换导致的组件卸载,可以结合react-router的useLocation,在路由变化时触发淡出动画,等动画结束后再完成路由跳转(或者用react-transition-group库简化实现)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:43:19