React MUI Accordion 始终保持至少一个面板展开问题求助
问题
使用Material-UI的Accordion组件时,需要实现始终至少有一个面板处于展开状态,不允许全部关闭,但现有代码仍可将所有面板关闭,无法达成需求。
现有代码:
import React, { useState } from "react"; import { Accordion, AccordionDetails, AccordionSummary, Typography } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; export default function DetailedAccordion() { const [expanded, setExpanded] = useState(null); const handleChange = (panel) => (event, isExpanded) => { setExpanded(isExpanded ? panel : false); }; return ( <div> <Accordion defaultExpanded expanded={expanded === "panel1"} onChange={handleChange("panel1")} > <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1bh-content" id="panel1bh-header" > <Typography>Accordion Panel 1</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. </Typography> </AccordionDetails> </Accordion> <Accordion expanded={expanded === "panel2"} onChange={handleChange("panel2")} > <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel2bh-content" id="panel2bh-header" > <Typography>Accordion Panel 2</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. </Typography> </AccordionDetails> </Accordion> <Accordion expanded={expanded === "panel3"} onChange={handleChange("panel3")} > <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel3bh-content" id="panel3bh-header" > <Typography>Accordion Panel 3</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. </Typography> </AccordionDetails> </Accordion> </div> ); }
解决方案
要实现始终至少一个面板展开,需修改状态初始化和面板切换逻辑,禁止关闭当前唯一展开的面板:
修改要点
- 初始化默认展开面板:将
expanded的初始值从null改为具体面板ID(如"panel1"),确保页面加载时就有面板展开 - 拦截关闭操作:在
handleChange中,判断如果是要关闭当前已展开的面板,则不执行状态更新;仅在切换到其他面板时更新状态
修改后的完整代码
import React, { useState } from "react"; import { Accordion, AccordionDetails, AccordionSummary, Typography } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; export default function DetailedAccordion() { // 初始化默认展开panel1 const [expanded, setExpanded] = useState("panel1"); const handleChange = (panel) => (event, isExpanded) => { // 若要关闭当前已展开的面板,直接返回不更新状态 if (!isExpanded && expanded === panel) { return; } // 切换到目标面板展开,或保持当前状态(如果是展开新面板) setExpanded(isExpanded ? panel : expanded); }; return ( <div> <Accordion expanded={expanded === "panel1"} onChange={handleChange("panel1")} > <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1bh-content" id="panel1bh-header" > <Typography>Accordion Panel 1</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. </Typography> </AccordionDetails> </Accordion> <Accordion expanded={expanded === "panel2"} onChange={handleChange("panel2")} > <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel2bh-content" id="panel2bh-header" > <Typography>Accordion Panel 2</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. </Typography> </AccordionDetails> </Accordion> <Accordion expanded={expanded === "panel3"} onChange={handleChange("panel3")} > <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel3bh-content" id="panel3bh-header" > <Typography>Accordion Panel 3</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. </Typography> </AccordionDetails> </Accordion> </div> ); }
逻辑说明
- 初始状态确保有面板展开,避免无面板的空白状态
- 点击当前展开的面板时,拦截关闭操作,保持该面板展开
- 点击其他面板时,正常切换展开状态,始终保证至少一个面板处于展开状态
内容的提问来源于stack exchange,提问作者JasSum
相关产品推荐
相关产品推荐

