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

React列表项位置平滑过渡:关闭展开框避免跳动

Got it, let's fix that jarring jump when collapsing your expandable list items! The issue with your current setup is that when expanded flips to false, React yanks the entire expandable div out of the DOM instantly—no time for the browser to animate a smooth transition. Here's how to fix this step by step:

Why your current approach doesn't work

Using {this.state.expanded && <div>Expandable</div>} means the element is either fully present or completely gone. CSS transitions can't animate "nothing to something" (or vice versa) because there's no element to apply the animation to. We need to keep the element in the DOM and animate its height instead.

Solution 1: For fixed-height expandable content

If your expandable section has a set height, this is super straightforward. Ditch the conditional rendering and use CSS transitions on height:

class ListItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = { expanded: false };
  }

  toggleExpand = () => {
    this.setState(prev => ({ expanded: !prev.expanded }));
  };

  render() {
    return (
      <div className="list-item-container">
        <button onClick={this.toggleExpand}>
          {this.state.expanded ? 'Collapse' : 'Expand'}
        </button>
        {/* Always render the expandable div, animate height */}
        <div
          className="expandable-section"
          style={{
            height: this.state.expanded ? '180px' : '0',
            overflow: 'hidden',
            transition: 'height 0.3s ease-in-out',
            background: '#f5f5f5',
            padding: this.state.expanded ? '16px' : '0 16px'
          }}
        >
          <p>Your fixed-height expandable content here</p>
          <p>More fixed content...</p>
        </div>
      </div>
    );
  }
}

Solution 2: For dynamic-height content (the flexible approach)

If your expandable content has variable height (like dynamic text, images, or components), we need to grab its natural height using a React ref. This way the animation will perfectly match the actual content size:

class DynamicListItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      expanded: false,
      contentHeight: 0 // Stores the natural height of the expandable content
    };
    this.expandableRef = React.createRef();
  }

  toggleExpand = () => {
    if (this.state.expanded) {
      // Collapse sequence: first set height to current scroll height, then animate to 0
      this.setState(
        { contentHeight: this.expandableRef.current.scrollHeight },
        () => {
          setTimeout(() => {
            this.setState({ contentHeight: 0, expanded: false });
          }, 10); // Tiny delay to let CSS register the height change
        }
      );
    } else {
      // Expand sequence: first show the content, then set height to its natural size
      this.setState({ expanded: true }, () => {
        this.setState({ contentHeight: this.expandableRef.current.scrollHeight });
      });
    }
  };

  // Optional: Update height if content changes while expanded
  componentDidUpdate(prevProps) {
    if (prevProps.content !== this.props.content && this.state.expanded) {
      this.setState({ contentHeight: this.expandableRef.current.scrollHeight });
    }
  }

  render() {
    const { expanded, contentHeight } = this.state;
    return (
      <div className="list-item-container">
        <button onClick={this.toggleExpand}>
          {expanded ? 'Collapse' : 'Expand'}
        </button>
        <div
          ref={this.expandableRef}
          className="expandable-section"
          style={{
            height: `${contentHeight}px`,
            overflow: 'hidden',
            transition: 'height 0.3s ease-in-out',
            background: '#f5f5f5',
            padding: expanded ? '16px' : '0 16px'
          }}
        >
          {/* Your dynamic content goes here */}
          {this.props.content}
        </div>
      </div>
    );
  }
}

Key tips to make this work

  • Never use display: none for the expandable element—this removes it from the layout entirely, so no animation can happen. Stick to height: 0 + overflow: hidden.
  • The tiny setTimeout in the collapse sequence is crucial: it gives the browser a moment to register the full height value before starting the transition to 0. Without it, the animation might skip.
  • If you want to animate other properties (like padding or opacity), add them to the transition property (e.g., transition: height 0.3s, padding 0.3s ease-in-out).

This approach will make your list items glide smoothly into place when expanding/collapsing, no more sudden jumps!

内容的提问来源于stack exchange,提问作者Björn Rave

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:06:20