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

实现React组件在过渡动画完成后显示/隐藏

Solution: Delay Component Unmount/Mount Until Transition Completes

Great question! Since you already have the height transition working, we just need to add a small state layer to separate the animation phase from the final mount/unmount state. Here's the simplest no-dependency approach tailored to your existing code:

Core Idea

Instead of directly using the collapsed prop to control whether the component returns null, we split the process into two intentional stages:

  • Collapse: Trigger the shrink animation first → wait for it to finish → then unmount the component
  • Expand: Mount the component first → wait for the DOM to render → then trigger the grow animation

Step-by-Step Implementation

Modified MyClass.tsx Code

We'll add internal state to track animation phases and listen for transition completion events:

import { useState, useEffect, useRef } from 'react';
import { cx } from 'classnames'; // assuming you're using classnames for className merging

export const MyClass = ({ items, collapsed = false, className, style, ...tcProps }: MyProps) => {
  // Controls if the component is rendered in the DOM at all
  const [isVisible, setIsVisible] = useState(!collapsed);
  // Tracks if we're actively running a collapse/expand animation
  const [isAnimating, setIsAnimating] = useState(false);
  const containerRef = useRef<HTMLDivElement>(null);

  // Listen for transition end events to finalize state
  useEffect(() => {
    const handleTransitionEnd = (e: TransitionEvent) => {
      // Only react to height transitions (ignore other properties if added later)
      if (e.propertyName !== 'height') return;

      // After collapse animation, hide the component
      if (collapsed) setIsVisible(false);
      setIsAnimating(false);
    };

    const container = containerRef.current;
    if (container) {
      container.addEventListener('transitionend', handleTransitionEnd);
    }

    // Clean up the listener when component unmounts or dependencies change
    return () => {
      if (container) {
        container.removeEventListener('transitionend', handleTransitionEnd);
      }
    };
  }, [collapsed]);

  // Handle animation triggers when the collapsed prop changes
  useEffect(() => {
    if (!collapsed) {
      // For expand: first render the component, then start the animation
      setIsVisible(true);
      // Wait for DOM to update before triggering the expand animation
      const timer = setTimeout(() => setIsAnimating(true), 0);
      return () => clearTimeout(timer);
    } else if (isVisible) {
      // For collapse: start the shrink animation before hiding the component
      setIsAnimating(true);
    }
  }, [collapsed, isVisible]);

  // Don't render anything if we've fully collapsed and hidden
  if (!isVisible) return null;

  return (
    <div
      ref={containerRef}
      id="Container"
      className={cx(
        cn.myClass,
        // Apply collapsed class only during active collapse animation
        (collapsed && isAnimating) && cn.myClassCollapsed,
        className
      )}
      style={{ ...tokens, ...style }}
      role="presentation"
    >
      <ClassChildren {...tcProps} style={{ width: '100%' }}>
        {supplyChildren()}
      </ClassChildren>
    </div>
  );
};

Key Changes Explained

  • isVisible State: Acts as the final toggle for whether the component renders. We only set this to false after the collapse animation finishes.
  • isAnimating State: Triggers the transition by applying the myClassCollapsed class during active animation phases.
  • transitionend Listener: Detects when the height animation completes, ensuring we only unmount the component after the visual effect finishes.
  • Timed Expand Trigger: Using setTimeout(0) ensures we wait for the component to mount in the DOM before starting the expand animation (so the browser has a base height value to animate from).

Your SCSS Remains Unchanged

Since you confirmed the transition already works, your existing styles don't need any updates:

@use 'utils/animation';
.my-class {
 transition: height animation.duration(500ms) animation.$default-timing-function;
 flex-shrink: 0;
 height: 9rem;
 overflow: hidden;
 margin-inline-start: 0;
 margin-inline-end: 0;
 padding-inline-start: 0;
 padding-inline-end: 0;
}
.my-class-collapsed {
 height: 0;
}

Edge Case Handling

  • Rapid Toggles: If the user clicks expand/collapse quickly before an animation finishes, the useEffect cleanup functions will remove old event listeners and clear timeouts, preventing state inconsistencies.
  • Multiple Transitions: The propertyName check ensures we only react to height changes, so adding other transitions (like opacity) later won't break the logic.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:08:11