如何在Next.js中通过useState控制ShadcnUI Accordion的展开与收起?
问题分析
你当前的问题有两个核心原因:
- 错误的组件结构:将
<Accordion>放在chapters.map循环中,导致每个章节生成独立的Accordion组件,彼此状态隔离,无法通过单个selectedChapter统一控制。 - 非受控模式限制:使用
defaultValue而非value绑定状态,defaultValue仅在组件初始渲染时生效,后续selectedChapter变更不会触发Accordion的状态更新。
解决方案
调整组件结构,将所有<AccordionItem>放在同一个<Accordion>下,并切换为受控模式,通过value绑定父组件传入的selectedChapter,同时通过onValueChange同步状态到父组件。
修改后的完整代码:
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; interface chapterProps { selectedChapter: number; setSelectedChapter: React.Dispatch<React.SetStateAction<number>>; chapters: any; } export default function Chapters(props: chapterProps) { return ( <> <div className="w-full flex flex-col space-y-4"> {/* 单个Accordion包裹所有Item */} <Accordion type="single" collapsible className="w-full bg-white p-4" // 绑定受控值,同步父组件状态 value={`item-${props.selectedChapter}`} // 当用户手动切换Accordion时,同步更新父组件的selectedChapter onValueChange={(selectedValue) => { const chapterIndex = parseInt(selectedValue.split("-")[1], 10); props.setSelectedChapter(chapterIndex); }} > {props.chapters?.map((value: any, index: number) => { return ( <AccordionItem value={`item-${index}`} key={index}> <AccordionTrigger> <div className="w-full text-left flex flex-col space-y-4"> <p className="mx-0 text-[#04445E] font-semibold text-2xl"> {value?.chaptername} </p> </div> </AccordionTrigger> <AccordionContent> {value?.chapterContent && value?.chapterContent?.map((content: any, i: number) => { return ( <div className="flex space-y-6 flex-col" key={i}> <p className="text-2xl text-[#011E29]"> {content.name} </p> <div className="flex space-x-4 text-[#011E29]"> <p>{content.type}</p> <p>{content.time}</p> </div> </div> ); })} </AccordionContent> </AccordionItem> ); })} </Accordion> </div> </> ); }
关键改动说明
- 调整结构:将
<Accordion>移到循环外部,所有章节的<AccordionItem>作为其直接子元素,确保单个Accordion组件统一管理所有Item的展开状态。 - 受控模式绑定:用
value={item-${props.selectedChapter}}替代defaultValue,让Accordion的状态完全由父组件传入的selectedChapter控制,状态变更时自动同步展开对应的Item。 - 同步用户操作:添加
onValueChange回调,当用户手动点击展开/收起时,解析选中的Item值并调用setSelectedChapter更新父组件状态,保证双向同步。
内容的提问来源于stack exchange,提问作者Dhyan TD
相关产品推荐
相关产品推荐

