如何使用React Hooks单独切换accordion手风琴面板?
手风琴单独切换解决方案
问题原因
你的代码存在3个核心问题导致所有面板同步切换:
- 所有面板共用了全局的激活状态、高度、旋转样式状态,没有和索引绑定,修改一个状态所有面板都会生效
- 命名逻辑混乱:useState返回的第一个值是状态本身,第二个是修改函数,你将状态值命名为
setActive极易引发逻辑判断错误 - 所有内容面板共用同一个ref,只能获取到最后一个面板的DOM属性,无法为每个面板计算独立高度
修复方案
仅保留「当前激活索引」这一个核心状态,所有面板的激活状态、样式、高度都通过循环索引和激活索引的对比动态生成,完全适配无唯一ID只用索引的场景。
修复后完整代码
import { useState, useRef } from "react"; const Accordion = (props) => { // 存储当前激活的索引,-1代表所有面板都关闭,需要默认打开第一个可改为0 const [activeIndex, setActiveIndex] = useState(-1); // 用数组存储每个内容面板的ref const contentRefs = useRef([]); const toggleAccordion = (index) => { // 如果点击的是当前激活的面板则关闭,否则切换为点击的面板 setActiveIndex(activeIndex === index ? -1 : index); }; return ( <div> {/* 若data为内部定义变量可替换为你自己的data变量 */} {props.data.map((item, index) => ( <div key={index} className="accordion__section"> <button className={`accordion ${activeIndex === index ? "active" : ""}`} onClick={() => toggleAccordion(index)} > <p className="accordion__title">{item.title}</p> <Chevron className={`accordion__icon ${activeIndex === index ? "rotate" : ""}`} width={10} fill={"#777"} /> </button> <div ref={(el) => (contentRefs.current[index] = el)} style={{ maxHeight: `${activeIndex === index ? contentRefs.current[index]?.scrollHeight + 'px' : '0px'}` }} className="accordion__content" > <div>{item.content}</div> </div> </div> ))} </div> ); };
关键修改说明
- 移除了冗余的高度、旋转样式状态,所有样式通过索引对比动态计算,天然和对应面板绑定
- 用ref数组存储每个内容面板的DOM引用,切换时可以精准获取对应面板的滚动高度实现平滑动画
- 切换逻辑仅修改激活索引值,所有面板自动根据索引匹配状态,不会出现同步切换的问题
内容的提问来源于stack exchange,提问作者Michael Morgan
相关产品推荐
相关产品推荐

