React侧边栏多模块独立展开折叠问题及代码优化需求
解决React侧边栏菜单同步展开/折叠与重复渲染问题
问题根源
- 同步展开/折叠:使用单个
isExpanded布尔状态控制所有模块,所有菜单共享同一状态 - 重复渲染:遍历
permisos数组渲染BarContainer,但BarContainer内部硬编码了三个模块,导致每个permisos项都渲染一遍完整菜单,最终重复三次
修复后的代码
import React, { useState } from 'react'; import { SideBarWrapper, Arrow } from './Sidebar.styles'; // 定义类型提升代码健壮性 type Seccion = { kms: number; sseccion: string; bedicion: boolean; }; type Modulo = { kmodulo: number; smodulo: string; secciones: Seccion[]; }; const Sidebar: React.FC = () => { // 用对象存储每个模块的展开状态,初始全部折叠 const [expandedModules, setExpandedModules] = useState<Record<string, boolean>>({ 'Laboratorio': false, 'Sucursales': false, 'Recursos Humanos': false }); // 单个模块的展开/折叠切换函数,接收模块名称作为参数 const toggleModule = (moduleName: string) => { setExpandedModules(prev => ({ ...prev, [moduleName]: !prev[moduleName] })); }; const permisos: Modulo[] = [ { kmodulo: 3, smodulo: 'Laboratorio', secciones: [ { kms: 10, sseccion: 'Atención a clientes', bedicion: true }, { kms: 15, sseccion: 'Caja', bedicion: true }, { kms: 14, sseccion: 'Paciente 360', bedicion: true }, ], }, { kmodulo: 3, smodulo: 'Sucursales', secciones: [ { kms: 10, sseccion: 'Caja - Cobro', bedicion: true }, { kms: 15, sseccion: 'Clientes', bedicion: true }, { kms: 14, sseccion: 'Convenios', bedicion: true }, { kms: 13, sseccion: 'Listas de Precio', bedicion: true }, { kms: 11, sseccion: 'Médicos', bedicion: true }, { kms: 12, sseccion: 'Pacientes', bedicion: true }, ], }, { kmodulo: 3, smodulo: 'Recursos Humanos', secciones: [ { kms: 10, sseccion: 'Caja - Cobro', bedicion: true }, { kms: 15, sseccion: 'Clientes', bedicion: true }, { kms: 14, sseccion: 'Convenios', bedicion: true }, { kms: 13, sseccion: 'Listas de Precio', bedicion: true }, { kms: 11, sseccion: 'Médicos', bedicion: true }, { kms: 12, sseccion: 'Pacientes', bedicion: true }, ], }, ]; return ( <SideBarWrapper> {/* 直接遍历permisos数组渲染每个模块,避免重复 */} {permisos.map((modulo) => ( <div key={modulo.smodulo}> <div className="menu-item" onClick={() => toggleModule(modulo.smodulo)}> <div className="menu-title">{modulo.smodulo}</div> {/* 根据当前模块的展开状态渲染箭头 */} <Arrow isExpanded={expandedModules[modulo.smodulo]} /> </div> {/* 仅当当前模块展开时渲染子菜单 */} {expandedModules[modulo.smodulo] && ( <div className="submenu"> {modulo.secciones.map((seccion) => ( <div className="submenu-item" key={seccion.kms}> {seccion.sseccion} </div> ))} </div> )} </div> ))} </SideBarWrapper> ); }; export default Sidebar;
关键改动说明
- 独立控制展开状态:将单个布尔状态
isExpanded改为对象expandedModules,以模块名称为键存储每个模块的展开状态,确保每个模块的展开/折叠操作互不影响 - 消除重复渲染:移除
BarContainer组件及内部硬编码的模块数据,直接遍历permisos数组渲染每个模块,避免重复渲染三次完整菜单 - 类型定义优化:添加
Seccion和Modulo类型,替代any类型,提升代码可读性和健壮性 - 子菜单key优化:使用
seccion.kms作为子菜单的key,比索引更稳定,符合React最佳实践
内容的提问来源于stack exchange,提问作者Diego Murillo
相关产品推荐
相关产品推荐

