skip to Main Content

I have this code for a sidebar, it has to render the 3 sections, "laboratorio", "sucursales" and "recursos humanos", the submenu of each one is taken from the sseccion of each const. However, I don’t know how to render the 3 and that each one has independent operation, since now the 3 menus are opened at the same time and are displayed multiple times.

import React, { useState } from 'react';
import { SideBarWrapper, Arrow } from './Sidebar.styles';

const Sidebar: React.FC = () => {
    const [isExpanded, setIsExpanded] = useState<boolean>(false);
    const toggleExpand = () => {
        setIsExpanded(!isExpanded);
    };

    const permisos = [
        {
            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 },
            ],
        },
    ];

    const BarContainer = (object: any) => {
        const lab = {
            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 },
            ],
        };

        const sucursales = {
            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 },
            ],
        };

        const recursoshumanos = {
            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>
                <div className="menu-item" onClick={toggleExpand}>
                    <div className="menu-title">{lab.smodulo}</div>
                    <Arrow isExpanded={isExpanded} />
                </div>
                {isExpanded && (
                    <div className="submenu">
                        {lab.secciones.map((seccion: any, index: number) => (
                            <div className="submenu-item" key={index}>
                                {seccion.sseccion}
                            </div>
                        ))}
                    </div>
                )}

                <div className="menu-item" onClick={toggleExpand}>
                    <div className="menu-title">{sucursales.smodulo}</div>
                    <Arrow isExpanded={isExpanded} />
                </div>
                {isExpanded && (
                    <div className="submenu">
                        {sucursales.secciones.map((seccion: any, index: number) => (
                            <div className="submenu-item" key={index}>
                                {seccion.sseccion}
                            </div>
                        ))}
                    </div>
                )}

                <div className="menu-item" onClick={toggleExpand}>
                    <div className="menu-title">{recursoshumanos.smodulo}</div>
                    <Arrow isExpanded={isExpanded} />
                </div>
                {isExpanded && (
                    <div className="submenu">
                        {recursoshumanos.secciones.map((seccion: any, index: number) => (
                            <div className="submenu-item" key={index}>
                                {seccion.sseccion}
                            </div>
                        ))}
                    </div>
                )}
            </SideBarWrapper>
        );
    };

    return (
        <>
            {permisos.map((permiso, index) => (
                <BarContainer key={index} lab={permiso} />
            ))}
        </>
    );
};

export default Sidebar;

The objective is to render the 3 sections, taking the data from the submenu of each of the const, which are dynamic. And that each one has independent operation, that each one can be opened and closed without affecting the other two.

2

Answers


  1. I understand your goal. Let’s modify the code to render the three sections independently, each with its own state for expansion. Here’s the revised version of your Sidebar component:

    import React, { useState } from 'react';
    import { SideBarWrapper, Arrow } from './Sidebar.styles';
    
    interface Section {
      kms: number;
      sseccion: string;
      bedicion: boolean;
    }
    
    interface Module {
      kmodulo: number;
      smodulo: string;
      secciones: Section[];
    }
    
    const Sidebar: React.FC = () => {
      const [expandedModules, setExpandedModules] = useState<{[key: string]: boolean}>({});
    
      const toggleExpand = (moduleName: string) => {
        setExpandedModules(prev => ({
          ...prev,
          [moduleName]: !prev[moduleName]
        }));
      };
    
      const modules: Module[] = [
        {
          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>
          {modules.map((module, index) => (
            <div key={index}>
              <div className="menu-item" onClick={() => toggleExpand(module.smodulo)}>
                <div className="menu-title">{module.smodulo}</div>
                <Arrow isExpanded={expandedModules[module.smodulo]} />
              </div>
              {expandedModules[module.smodulo] && (
                <div className="submenu">
                  {module.secciones.map((seccion, sIndex) => (
                    <div className="submenu-item" key={sIndex}>
                      {seccion.sseccion}
                    </div>
                  ))}
                </div>
              )}
            </div>
          ))}
        </SideBarWrapper>
      );
    };
    
    export default Sidebar;
    

    Here’s what I’ve changed:

    1. I’ve combined all the modules (Laboratorio, Sucursales, Recursos Humanos) into a single array called modules. This makes it easier to render them dynamically.

    2. Instead of a single isExpanded state, I’ve created an expandedModules object state. This object keeps track of the expanded state for each module independently.

    3. The toggleExpand function now takes a module name as an argument and toggles the state for that specific module.

    4. In the render method, I’m mapping over the modules array to create each section of the sidebar.

    5. Each section now has its own onClick handler that calls toggleExpand with the module name.

    6. The Arrow component and the submenu rendering now depend on the expanded state of each individual module.

    This implementation allows each section to be opened and closed independently, and the submenus are populated dynamically from the secciones array of each module.

    Login or Signup to reply.
  2. Issue

    You have used only a single boolean state that controls all the menus you want to toggle open/close.

    const [isExpanded, setIsExpanded] = useState<boolean>(false);
    const toggleExpand = () => {
      setIsExpanded(!isExpanded);
    };
    

    All menu sections get toggled together at the same time.

    Solution

    Update the state to hold an object of boolean values to independently toggle specific sections.

    • Update the isExpanded state from a single boolean value to a map/look object Record<string, boolean>.

    • Update the toggleExpand handler to consume a unique section identifier so just that section’s expanded state can be toggled. The smodulo property seems to be a good candidate.

      const toggleExpand = (smodulo: string) => () => {
        setIsExpanded(isExpanded => ({
          ...isExpanded,
          [smodulo]: !isExpanded[smodulo],
        }));
      };
      
    • Check for what is expanded using isExpanded[section.smodulo].

    • Move the static permisos data out of the Sidebar component.

    • Don’t declare React components within other React components, so remove the nested BarContainer component declaration.

    • Make the code more DRY (Don’t Repeat Yourself) by mapping the permisos array of the sections to JSX, then map each section’s secciones array of items.

    • Avoid using array indices as React keys, use a unique property value intrinsic to the data, e.g. section.smodulo for the sections and seccion.sseccion for the items.

    Example:

    const permisos = [
      {
        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 },
        ],
      },
    ];
    
    const Sidebar = () => {
      const [isExpanded, setIsExpanded] = useState<Record<string, boolean>>({});
    
      const toggleExpand = (smodulo: string) => () => {
        setIsExpanded(isExpanded => ({
          ...isExpanded,
          [smodulo]: !isExpanded[smodulo],
        }));
      };
    
      return (
        <SideBarWrapper>
          {permisos.map(section => (
            <React.Fragment key={section.smodulo}>
              <div className="menu-item" onClick={toggleExpand(section.smodulo)}>
                <div className="menu-title">{section.smodulo}</div>
                <Arrow isExpanded={isExpanded[section.smodulo]} />
              </div>
              {isExpanded[smodulo] && (
                <div className="submenu">
                  {section.secciones.map((seccion) => (
                    <div className="submenu-item" key={seccion.sseccion}>
                      {seccion.sseccion}
                    </div>
                  ))}
                </div>
              )}
            </React.Fragment>
          ))}
        </SideBarWrapper>
      );
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search