在Next.js中实现MUI分离式自定义手风琴组件遇阻求助
分离式MUI手风琴实现方案
核心思路
放弃使用React Portal(易导致多面板内容挂载到同一DOM节点引发合并问题),将标题栏与内容区拆分为两个独立容器,通过全局状态关联面板的展开/收起状态,实现分离式布局的同时保证内容独立渲染。
具体实现步骤
- 维护展开状态:用对象存储每个面板的展开状态,键为面板唯一ID,值为布尔值。
- 标题栏容器:遍历数据渲染标题组件,点击时切换对应面板的状态。
- 内容区容器:仅渲染当前展开的面板内容,通过布局确保与标题栏位置对齐。
- 样式对齐:通过CSS或MUI布局组件(如Grid)保证标题与内容的对应关系。
代码示例
状态与事件处理
import { useState } from 'react'; import { AccordionSummary, AccordionDetails, Typography, ExpandMoreIcon, Grid } from '@mui/material'; const SeparateAccordion = ({ data }) => { // 存储各面板展开状态,key为面板ID const [expandedPanels, setExpandedPanels] = useState({}); // 切换面板展开状态 const togglePanel = (panelId) => { setExpandedPanels(prev => ({ ...prev, [panelId]: !prev[panelId] })); }; return ( <Grid container spacing={2}> {/* 左侧标题栏 */} <Grid item xs={3}> <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}> {data.map(item => ( <AccordionSummary key={item.id} onClick={() => togglePanel(item.id)} expandIcon={<ExpandMoreIcon />} aria-expanded={expandedPanels[item.id] || false} sx={{ cursor: 'pointer', border: '1px solid #e0e0e0', borderRadius: '4px' }} > <Typography>{item.title}</Typography> </AccordionSummary> ))} </div> </Grid> {/* 右侧内容区 */} <Grid item xs={9}> <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}> {data.map(item => ( expandedPanels[item.id] && ( <AccordionDetails key={item.id} sx={{ border: '1px solid #e0e0e0', borderRadius: '4px', padding: '16px' }} > <Typography>{item.content}</Typography> </AccordionDetails> ) ))} </div> </Grid> </Grid> ); };
数据格式示例
const demoData = [ { id: 'panel-1', title: '面板1', content: '面板1的详细内容...' }, { id: 'panel-2', title: '面板2', content: '面板2的详细内容...' }, { id: 'panel-3', title: '面板3', content: '面板3的详细内容...' }, ];
关键说明
- 每个面板必须有唯一ID,确保状态切换和渲染的准确性。
- 内容区通过
expandedPanels[item.id]判断是否渲染,避免未展开面板占用布局空间。 - 若需纵向排列的分离式布局(标题在上、内容在下),可去掉Grid,将标题栏和内容区改为垂直排列的容器即可。
内容的提问来源于stack exchange,提问作者CADENTIC
相关产品推荐
相关产品推荐

