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

如何在React中实现支持高度动态CSS过渡效果的基础Accordion组件

实现方案

由于高度过渡需要同步操作DOM、触发浏览器重排才能生效,完全依赖React异步的setState更新必然无法满足时序要求,因此我们直接通过ref操作DOM节点实现过渡逻辑,仅用state存储最终的展开/收起状态即可,完美复现原生JS的效果,同时支持动态内容高度自适应。

必要CSS样式

.content {
  overflow: hidden;
  transition: height 0.3s ease;
  height: 0;
}
.content.open {
  height: auto;
}
.content.block {
  display: block !important;
}

完整React类组件实现

import "./app.css";
import React, { Component } from "react";

class App extends Component {
  state = {
    open: true
  };
  // 绑定内容区域DOM引用
  contentRef = React.createRef();

  componentDidMount() {
    // 绑定过渡结束事件,注意事件名是全小写transitionend(原代码大写E是错误写法)
    this.contentRef.current.addEventListener("transitionend", this.handleTransitionEnd);
  }

  componentWillUnmount() {
    // 组件卸载前解绑事件,避免内存泄漏
    this.contentRef.current.removeEventListener("transitionend", this.handleTransitionEnd);
  }

  // 过渡结束后重置样式
  handleTransitionEnd = () => {
    const content = this.contentRef.current;
    content.classList.remove("block");
    content.style.height = null;
  }

  toggle = () => {
    const content = this.contentRef.current;
    const wasOpen = this.state.open;
    // 仅用state存储最终开关状态
    this.setState({ open: !wasOpen });

    // 强制显示元素,保证可以读取到正确的内容高度
    content.classList.add("block");
    // 读取当前内容的实际高度,动态内容更新后也能拿到最新高度
    const fullHeight = content.offsetHeight;

    if (wasOpen) {
      // 收起逻辑:先把高度设为当前实际高度
      content.style.height = `${fullHeight}px`;
      // 强制浏览器重排,确保上面的高度设置生效
      void content.offsetWidth;
      // 设为0触发过渡动画
      content.style.height = "0px";
    } else {
      // 展开逻辑:先把高度设为0
      content.style.height = "0px";
      // 强制浏览器重排,确保上面的高度设置生效
      void content.offsetWidth;
      // 设为实际内容高度触发过渡动画
      content.style.height = `${fullHeight}px`;
    }
  };

  render() {
    return (
      <React.Fragment>
        <button className="button" onClick={this.toggle}>
          切换
        </button>
        <div 
          className={`content ${this.state.open ? "open" : ""}`} 
          ref={this.contentRef}
        >
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </React.Fragment>
    );
  }
}

export default App;

函数组件版本(可选)

如果使用React 16.8+的函数组件,实现逻辑完全一致:

import "./app.css";
import React, { useRef, useState, useEffect } from "react";

const App = () => {
  const [open, setOpen] = useState(true);
  const contentRef = useRef(null);

  useEffect(() => {
    const content = contentRef.current;
    const handleTransitionEnd = () => {
      content.classList.remove("block");
      content.style.height = null;
    };
    content.addEventListener("transitionend", handleTransitionEnd);
    return () => content.removeEventListener("transitionend", handleTransitionEnd);
  }, []);

  const toggle = () => {
    const content = contentRef.current;
    const wasOpen = open;
    setOpen(!wasOpen);

    content.classList.add("block");
    const fullHeight = content.offsetHeight;

    if (wasOpen) {
      content.style.height = `${fullHeight}px`;
      void content.offsetWidth;
      content.style.height = "0px";
    } else {
      content.style.height = "0px";
      void content.offsetWidth;
      content.style.height = `${fullHeight}px`;
    }
  };

  return (
    <>
      <button className="button" onClick={toggle}>切换</button>
      <div 
        className={`content ${open ? "open" : ""}`} 
        ref={contentRef}
      >
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
      </div>
    </>
  );
};

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 08:24:03