rsuite Accordion 手风琴组件实战指南:从默认用法到受控模式与无障碍设计
发布时间:2026/9/25 3:39:15来源:尧图网络
前端UI组件【免费下载链接】rsuite A suite of React components .项目地址https://gitcode.com/gh_mirrors/rs/rsuite点击查看免费下载rsuite 的Accordion手风琴组件用于通过点击标题来展开与折叠内容区域非常适合在有限的页面空间内集中展示大量内容例如 FAQ 列表、设置分组、文档目录等。本文以该组件的官方文档为核心结合仓库中的组件源码与示例系统讲解默认多面板展开、单面板展开、受控模式、自定义标题/指示器、禁用面板以及无障碍ARIA 与键盘交互等完整用法帮助你在实际项目中快速上手并正确配置。阅读完本文后你将能够编写出可复制的 Accordion 代码并理解其底层状态管理机制。组件概述Accordion 是 rsuite 中典型的可折叠内容展示组件面板标题作为触发区点击后在展开与收起之间切换正文区域通过动画平滑地显示或隐藏。它的典型应用场景包括在有限空间内承载大量分组内容、构建常见问题FAQ列表、组织设置面板等。在 rsuite 中Accordion本质上是对PanelGroup的封装从源码 src/Accordion/Accordion.tsx 可以看到Accordion直接以accordion模式渲染PanelGroup并通过Subcomponents挂载了Panel子组件const Accordion forwardRefdiv, AccordionProps, typeof Subcomponents((props, ref) { const { propsWithDefaults } useCustom(Accordion, props); return PanelGroup accordion ref{ref} {...propsWithDefaults} /; }, Subcomponents);也就是说Accordion的AccordionProps类型即OmitPanelGroupProps, accordion其全部对外 API 都来自 PanelGroup而Accordion.Panel则是 src/Accordion/AccordionPanel.tsx 中直接复用的Panel组件。理解了这一层封装关系后续所有属性与行为就都能在PanelGroup/Panel源码中找到依据。获取组件与 rsuite 其他组件一致Accordion可通过命名导入方式使用import { Accordion, Placeholder } from rsuite;示例中使用的Placeholder组件用于在演示与骨架场景下快速生成占位内容Placeholder.Paragraph /正式开发时按需替换为真实业务内容即可。基础用法默认多面板展开Accordion的默认行为是可以同时展开多个面板点击任一面板标题即可展开或收起其内容区。官方文档提供的入门示例位于 docs/pages/components/accordion/fragments/basic.mdimport { Accordion, Placeholder } from rsuite; const App () ( Accordion Accordion.Panel headerAccordion Panel 1 defaultExpanded Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 2 Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 3 Placeholder.Paragraph / /Accordion.Panel /Accordion ); ReactDOM.render(App /, document.getElementById(root));要点说明header是面板标题点击它即可切换面板的展开状态。defaultExpanded让第一个面板在初始渲染时即处于展开状态。默认情况下各面板的展开状态相互独立互不影响。从源码看默认行为的实现位于 src/Panel/hooks/useExpanded.ts每个Panel通过useControlled管理自己的展开状态只有当accordion模式即Accordion组件下存在activeKey时面板间才会产生联动const [expandedState, setExpanded] useControlledany( expandedProp, defaultExpanded || (typeof activeKey ! undefined activeKey eventKey) ); if (accordion) { collapsible true; } if (collapsible) { if (typeof activeKey ! undefined activeKey ! eventKey) { expanded false; } }也就是说只要不设置activeKey/defaultActiveKey每个面板就各自管理自身的expanded状态从而实现多面板同时展开。带边框样式通过bordered属性可以为整个 Accordion 添加边框使面板之间的分隔更加清晰。官方示例见 docs/pages/components/accordion/fragments/bordered.mdimport { Accordion, Placeholder } from rsuite; const App () ( Accordion bordered Accordion.Panel headerAccordion Panel 1 defaultExpanded Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 2 Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 3 Placeholder.Paragraph / /Accordion.Panel /Accordion ); ReactDOM.render(App /, document.getElementById(root));在源码中bordered会同时作用于PanelGroup与Panel的 CSS 类名见 src/PanelGroup/PanelGroup.tsx 中的withPrefix({ accordion, bordered })与 src/Panel/Panel.tsx 中的withPrefix({ in: expanded, collapsible, bordered, shaded })对应样式定义在 src/PanelGroup/styles/index.scss 与 src/Panel/styles/index.scss。在 Storybook 中同样可以通过bordered: true复现该形态见 src/Accordion/stories/Accordion.stories.tsx 中的Bordered故事。只展开一个面板手风琴模式的核心当设置了defaultActiveKey或activeKey后Accordion切换为经典的手风琴模式同一时刻只能有一个面板处于展开状态展开新面板会自动收起上一个。官方示例见 docs/pages/components/accordion/fragments/accordion.mdimport { Accordion, Placeholder } from rsuite; const App () ( Accordion defaultActiveKey{1} bordered Accordion.Panel headerAccordion Panel 1 eventKey{1} Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 2 eventKey{2} Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 3 eventKey{3} Placeholder.Paragraph / /Accordion.Panel /Accordion ); ReactDOM.render(App /, document.getElementById(root));要点说明每个Accordion.Panel通过eventKey声明自己的唯一标识eventKey支持string或number类型。defaultActiveKey指定初始展开面板对应的eventKey本例中传入1故第一个面板默认展开。activeKey与defaultActiveKey均可触发单面板模式二者的区别在于activeKey是完全受控的见下文。其底层联动逻辑同样在 src/Panel/hooks/useExpanded.ts 中在accordion模式下所有面板共享PanelGroupContext中的activeKey当某个面板的eventKey与activeKey不一致时其expanded会被强制置为false同时通过useEffect将activeKey eventKey的结果同步回面板自身的展开状态useEffect(() { if (accordion typeof activeKey ! undefined) { setExpanded(activeKey eventKey); } }, [accordion, activeKey, eventKey, setExpanded]);而activeKey的更新则由PanelGroup中的handleSelect统一处理见 src/PanelGroup/PanelGroup.tsxconst handleSelect useEventCallback( (activeKey: KeyType | undefined, event: React.MouseEvent) { setActiveKey(activeKey); onSelect?.(activeKey, event); } );受控组件activeKey 与 onSelect对于需要将展开状态纳入业务逻辑例如与路由、搜索高亮联动的场景可以使用activeKeyonSelect构建完全受控的手风琴。官方示例见 docs/pages/components/accordion/fragments/controlled.mdimport { Accordion, Placeholder, ButtonGroup, Button } from rsuite; const App () { const [activeKey, setActiveKey] React.useState(1); return ( ButtonGroup {[1, 2, 3].map(key ( Button key{key} active{key activeKey} onClick{() setActiveKey(key)} Expand Item {key} /Button ))} /ButtonGroup hr / Accordion activeKey{activeKey} bordered onSelect{setActiveKey} Accordion.Panel headerAccordion Panel 1 eventKey{1} Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 2 eventKey{2} Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 3 eventKey{3} Placeholder.Paragraph / /Accordion.Panel /Accordion / ); }; ReactDOM.render(App /, document.getElementById(root));该示例展示了一个完整的双向控制闭环组件内部点击面板标题时PanelGroup.handleSelect会调用外部传入的onSelect将新的eventKey回调给setActiveKey从而更新 React 状态。外部通过按钮组修改activeKey再以activeKey属性回传给 Accordion。Panel端通过useExpanded中的useControlled(activeProp, defaultActiveKey)读取受控值实现展开状态的同步。这正是 rsuite 受控组件的一贯写法activeKey由外部状态唯一驱动onSelect负责把内部交互上报给外部。若只需要初始值、后续交给组件自身管理则使用defaultActiveKey非受控。自定义指示器Accordion.Panel的caretAs属性允许替换标题右侧的默认展开指示箭头传入任意 React 组件即可。官方示例见 docs/pages/components/accordion/fragments/custom-indicator.md此处使用react-icons/fa的图标作为演示import { Accordion, Placeholder } from rsuite; import { FaAngleDoubleDown, FaArrowAltCircleDown, FaArrowDown } from react-icons/fa; const App () ( Accordion defaultActiveKey{1} bordered Accordion.Panel headerAccordion Panel 1 eventKey{1} caretAs{FaAngleDoubleDown} Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 2 eventKey{2} caretAs{FaArrowAltCircleDown} Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 3 eventKey{3} caretAs{FaArrowDown} Placeholder.Paragraph / /Accordion.Panel /Accordion ); ReactDOM.render(App /, document.getElementById(root));从源码看caretAs的渲染位置在 src/Panel/PanelHeader.tsx当面板可折叠collapsible时标题会包装为AccordionButton并把caretAs作为指示图标传入{collapsible ? ( AccordionButton id{buttonId} role{role} caretAs{caretAs} controlId{bodyId} disabled{disabled} expanded{expanded} onClick{onClickButton} {headerElement} /AccordionButton ) : ( headerElement )}因此caretAs的实际类型是React.ElementType见 src/Panel/Panel.tsx 中的caretAs?: React.ElementType可以接收任意图标或自定义组件。在 Storybook 中也有对应的CustomIndicator故事src/Accordion/stories/Accordion.stories.tsx使用的是 rsuite 自带的rsuite/icons图标。自定义标题header属性接受任意ReactNode因此可以自由组合头像、多行文本等富内容作为面板标题。官方示例见 docs/pages/components/accordion/fragments/custom-header.md它结合Stack与Avatar构建了一个头像 标题 副标题的复杂标题import { Accordion, Placeholder, Stack, Avatar } from rsuite; const Header props { const { avatarUrl, title, subtitle, ...rest } props; return ( Stack {...rest} spacing{10} alignItemsflex-start Avatar src{avatarUrl} alt{title} / Stack spacing{2} directioncolumn alignItemsflex-start div{title}/div div style{{ color: var(--rs-text-secondary), fontSize: 12 }}{subtitle}/div /Stack /Stack ); }; const App () ( Accordion bordered defaultActiveKey{1} Accordion.Panel header{ Header avatarUrlhttps://avatars.githubusercontent.com/u/6412038 titleReact subtitleThe library for web and native user interfaces / } eventKey{1} React is a JavaScript library for building user interfaces. /Accordion.Panel {/* 其余 Panel 同理可传入 Vue、Angular 等不同标题内容 */} /Accordion ); ReactDOM.render(App /, document.getElementById(root));实现层面的细节在 src/Panel/PanelHeader.tsx 中当header传入的是单个合法 React 元素非数组、非 Fragment时PanelHeader会通过cloneElement将rs-panel-title类名合并到该元素上从而保证标题区域在可折叠按钮内的布局与样式正确if (!isValidElement(children) || Array.isArray(children) || isFragment(children)) { headerElement div className{prefix(title)}{children}/div; } else { const className merge(prefix(title), get(children, props.className)); headerElement cloneElementany(children, { className }); }因此自定义标题既可以是纯文本也可以是任意组件无需额外适配。禁用面板Accordion.Panel的disabled属性用于禁用单个面板禁用后标题不可点击、面板不可展开。官方示例见 docs/pages/components/accordion/fragments/disabled-panel.mdimport { Accordion, Placeholder } from rsuite; const App () ( Accordion defaultActiveKey{1} bordered Accordion.Panel headerAccordion Panel 1 eventKey{1} Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 2 eventKey{2} disabled Placeholder.Paragraph / /Accordion.Panel Accordion.Panel headerAccordion Panel 3 eventKey{3} Placeholder.Paragraph / /Accordion.Panel /Accordion ); ReactDOM.render(App /, document.getElementById(root));disabled在渲染链路中的传递路径为Panel→PanelHeader→AccordionButton见 src/Panel/PanelHeader.tsx最终渲染为带disabled语义的按钮使点击与键盘操作均失效。在 Storybook 中可通过Disabled故事整体观察效果src/Accordion/stories/Accordion.stories.tsx。可访问性Accordion 组件对无障碍支持进行了专门设计官方文档见 docs/pages/components/accordion/en-US/index.md 与 中文版给出了明确的 ARIA 属性与键盘交互约定。ARIA 属性aria-expanded表示面板当前处于展开还是折叠状态。aria-controls标识由该面板标题所控制的内容区域。aria-labelledby标识作为面板标题的元素。aria-disabled标识面板处于禁用状态。这些属性的实际接线可以在源码中找到在 src/Panel/Panel.tsx 中每个面板会通过useUniqueId生成唯一 id并派生bodyId内容区 id与buttonId标题按钮 idconst id useUniqueId(rs-, idProp); const bodyId ${id}-panel; const buttonId ${id}-btn;PanelHeader将buttonId、controlId{bodyId}、expanded、disabled传入AccordionButton最终在按钮元素上输出aria-controls指向内容区、aria-expanded、aria-disabled等属性实现标题与内容区之间的无障碍关联。键盘交互Tab移动焦点到下一个可聚焦的面板。Enter或Space展开或折叠当前聚焦的面板。由于标题区域最终渲染为原生按钮元素这些键盘行为由浏览器原生支持无需额外编写键盘事件处理。Props 参考以下属性表整理自官方文档docs/pages/components/accordion/en-US/index.md并结合源码补充了类型细节。Accordion属性类型(默认值)描述activeKeystring激活项的事件键受控。borderedboolean显示边框。classPrefixstring组件 CSS 类名的前缀。defaultActiveKeystring默认激活项的事件键非受控。onSelect(eventKey: string, event) void激活项变化时的回调。注Accordion的类型定义是OmitPanelGroupProps, accordion因此它还继承了PanelGroup/Box的其他通用属性如as、className、children等。从源码 src/PanelGroup/PanelGroup.tsx 看activeKey、defaultActiveKey的底层类型实际为string | numberKeyType官方文档以string简化表述相应地onSelect回调签名中的eventKey也是T | undefined。Accordion.Panel属性类型(默认值)描述bodyFillboolean内容区域是否填充配合容器背景。caretAsReactNode自定义指示器实际类型为React.ElementType。classPrefixstring(panel)组件 CSS 类名的前缀。defaultExpandedboolean默认展开面板。disabledboolean禁用面板。eventKeystring面板对应的事件键支持string \| number。expandedboolean展开面板受控。headerReactNode面板标题支持任意 React 节点。注Accordion.Panel的类型定义即PanelProps见 src/Accordion/AccordionPanel.tsx因此Panel的其余属性如collapsible、shaded、scrollShadow、bodyProps、id等同样可用。设计指引与最佳实践结合官方文档描述与源码实现在实际项目中使用 Accordion 时可以参考以下实践按内容量选择展开模式内容彼此独立、希望用户同时对照查看时保持默认的多面板展开希望节省空间、引导用户聚焦单一内容如设置分组、FAQ时通过defaultActiveKey或activeKey开启单面板模式。优先使用非受控按需受控仅需初始展开哪个面板时使用defaultActiveKeyeventKey需要把展开状态与外部状态路由、URL 参数、搜索关键词等双向同步时才使用activeKeyonSelect的受控写法。善用事件键的类型一致性eventKey支持string | number但请保证defaultActiveKey/activeKey与各面板eventKey的类型一致否则不会命中对应的展开面板。自定义标题注意可点击区域传入富内容标题时PanelHeader会把rs-panel-title类名合并到自定义元素上保持标题布局与样式正确同时应避免在标题内嵌套交互元素防止点击冲突。无障碍不额外开发标题渲染为原生按钮aria-expanded、aria-controls、aria-disabled以及Tab/Enter/Space键盘交互均由组件内置无需手工补充自定义标题时保持标题内容可读即可。小结本文从官方文档的入门示例出发完整覆盖了 rsuiteAccordion的默认多面板展开、bordered边框、activeKey/defaultActiveKey单面板模式、activeKeyonSelect受控用法、caretAs自定义指示器、header自定义标题、disabled禁用面板以及 ARIA 与键盘交互等无障碍特性并深入 src/Accordion、src/PanelGroup/PanelGroup.tsx、src/Panel/Panel.tsx 与 src/Panel/hooks/useExpanded.ts 等源码解释了其状态管理与渲染机制。你可以直接参考本文中的代码片段快速落地也可以继续阅读 src/Accordion/stories/Accordion.stories.tsx 中的 Storybook 故事了解各组件的可视化组合效果。赞分享前端UI组件【免费下载链接】rsuite A suite of React components .项目地址https://gitcode.com/gh_mirrors/rs/rsuite点击查看免费下载相关推荐如何用TestDisk快速找回丢失的分区10步完整教程如何用TestDisk快速找回丢失的分区10步完整教程 TestDisk是一款强大的开源数据恢复工具专门用于检查磁盘的分区和引导扇区在恢复丢失分区方面表现存储Quasar QExpansionItem 折叠面板组件完全指南从基础用法、手风琴模式到无障碍实现Quasar QExpansionItem 折叠面板组件完全指南从基础用法、手风琴模式到无障碍实现 QExpansionItem 是 Quasar 框架当前前端UI组件跨平台TVBoxOSC 电视盒子管理工具免费快速完成部署的完整教程TVBoxOSC 电视盒子管理工具免费快速完成部署的完整教程 家里刚装好的电视盒子开机却满屏广告、菜单复杂、找半天想看的内容想换一套干净顺手的盒子管理方案上一篇NetCoreMicroservicesSample单元测试实战CQRS命令处理测试技巧下一篇163MusicLyrics一站式智能歌词提取工具如何重塑你的音乐体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网