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

如何实现点击单个手风琴组件时,阻止其他手风琴组件展开?

解决手风琴组件互斥展开问题

你的问题根源在于每个AccordionItemFAQ组件各自维护独立的isOpen状态,它们之间无法同步状态,所以做不到点击一个时关闭其他。正确的做法是把状态提升到父组件(FAQ页面)统一管理,通过控制当前展开的FAQ ID来实现互斥效果。

第一步:修改AccordionItemFAQ组件

移除组件内部的isOpen状态,改为通过props接收外部传入的展开状态和点击事件:

import { memo } from "react";
import styles from "./AccordionItemFAQ.module.scss";

interface AccordionItemFAQProps {
    title: string;
    content: string;
    isOpen: boolean;
    onClick: () => void;
}

const AccordionItemFAQ: React.FC<AccordionItemFAQProps> = ({ title, content, isOpen, onClick }) => {
    return (
        <div className={styles.containerAll}>
            <button onClick={onClick} className={styles.containerTitle}>
                {title}
            </button>
            {isOpen && (
                <div className={styles.containerContent}>
                    <div className={styles.splitter}></div>
                    <div className={styles.content}>{content}</div>
                </div>
            )}
        </div>
    );
};

export default memo(AccordionItemFAQ);

第二步:修改FAQ父组件

在父组件中添加状态管理当前展开的FAQ ID,渲染每个AccordionItemFAQ时传入对应的展开状态和点击回调:

import styles from "styles/pages/FAQ.module.scss";
import type { LayoutProps } from "@root/layouts";
import { buildMainLayout } from "@root/layouts";
import { useEffect, useState } from "react";
import { FaqTable } from "@root/constants/types";
import axios from "axios";
import AccordionItemFAQ from "@root/components/AccordionItemFAQ";

export default function Faq() {
    const [faqTable, setFaqTable] = useState<FaqTable[]>([]);
    const [openFaqId, setOpenFaqId] = useState<number | null>(null); // 记录当前展开的FAQ ID
    const api = "http://localhost:8088/api/v1/faq";

    const loadFaqTable = () => {
        axios.get(api).then((response) => setFaqTable(response.data));
    };

    useEffect(() => {
        loadFaqTable();
    }, []);

    const handleAccordionClick = (faqId: number) => {
        // 如果点击的是已经展开的,就关闭;否则展开当前的
        setOpenFaqId(openFaqId === faqId ? null : faqId);
    };

    return (
        <div>
            <h1>FAQ</h1>

            <div className={styles.parentContainer}>
                {faqTable.map((item) => (
                    <AccordionItemFAQ
                        key={item.faqId}
                        title={item.question}
                        content={item.answer}
                        isOpen={openFaqId === item.faqId}
                        onClick={() => handleAccordionClick(item.faqId)}
                    ></AccordionItemFAQ>
                ))}
            </div>
        </div>
    );
}

// <-- layout
const layoutProps: LayoutProps = {
    mainClassName: "faq",
    currentPageDescription: "faqPage",
};
Faq.layout = buildMainLayout(layoutProps);

原理说明

  • 父组件通过openFaqId状态唯一标记当前展开的FAQ项,所有子组件的展开状态都由这个值决定
  • 点击某个手风琴项时,父组件更新openFaqId:如果是当前展开的项,则置为null(关闭);否则设为该项的ID(展开该项,同时自动关闭其他项)
  • 这种状态提升的方式是React中处理组件间状态联动的标准方案,避免了子组件状态孤立的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 10:33:34