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

如何在Next.js中通过useState控制ShadcnUI Accordion的展开与收起?

问题分析

你当前的问题有两个核心原因:

  1. 错误的组件结构:将<Accordion>放在chapters.map循环中,导致每个章节生成独立的Accordion组件,彼此状态隔离,无法通过单个selectedChapter统一控制。
  2. 非受控模式限制:使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:07:47