Fermer

novembre 24, 2021

Comment créer un menu latéral récursif dans React


Dans ce didacticiel, vous apprendrez à créer un menu de navigation latéral imbriqué à l'aide de composants récursifs. Nous verrons également comment styliser les liens de navigation actifs et créer une mise en page à l'aide d'une grille CSS.

De nombreux types d'applications peuvent nécessiter la création de composants récursifs. Si vous avez vu au moins quelques thèmes d'interface utilisateur d'administration, vous avez peut-être remarqué que beaucoup d'entre eux ont souvent une barre latérale contenant un menu de navigation avec des liens imbriqués. Dans ce tutoriel, je veux vous montrer comment créer un menu récursif dans React. Ci-dessous, vous pouvez voir un GIF du menu que nous allons créer.

 Un menu latéral récursif avec des éléments de navigation Accueil, Profil, Paramètres. Les paramètres se développent pour inclure le compte ; Security > Credentials, 2FA." title="Recursive side menu"/></p>
<p>Commençons par une configuration de projet.</p>
<h2 id=Project Setup

Pour ce tutoriel, j'ai décidé d'utiliser Vite. Vous pouvez échafauder un nouveau projet avec npm ou Yarn.

Avec npm

npm init @vitejs/app recursive-menu --template react

Avec Yarn

yarn create @vitejs/app recursive-menu --template react

Une fois le projet créé, accédez au répertoire du projet :

cd ./recursive-menu

Et installer les dépendances ainsi que la bibliothèque react-router-dom

Avec npm

npm install react-router-dom

Avec fil

 fil ajouter react-router-dom

Ensuite, nettoyez les fichiers App.jsx et App.css. Vous pouvez tout supprimer du fichier App.css. Ci-dessous, vous pouvez voir à quoi devrait ressembler votre fichier App.jsx :

import React from 'react';
import '. /App.css';

function App() {
  return <div className=[19659021]"App"></div>;
}

export default  Application;

Après cela, vous pouvez démarrer le serveur de développement en exécutant npm run dev ou yarn dev.

Layout and Routes Setup

Avant de nous concentrer sur la création d'un menu latéral récursif, je veux vous montrer comment créer une mise en page à l'aide d'une grille CSS. Une fois la mise en page prête, nous commencerons à travailler sur le menu de la barre latérale.

Commençons par créer un composant de mise en page. Il rendra les éléments d'en-tête, de côté, principal et de pied de page.

src/layout/Layout.jsx

import React from 'react' ;
import style from './layout.module.css';

const Layout = accessoires =>  {
  const { enfants } = accessoires;

  retour (
  	<div[19659032]className={style.layout}>
      <header className=[19659021]{style.header}></header>
      <side  className={style.aside}></aside>
      <principal[1 9659032]className={style.main}>{enfants}[19659087]</main>
      <footer className={style.footer}></footer>
    </div>
  );
};

 export default Layout;

Comme vous pouvez le voir dans le code, nous utilisons des modules CSS. Les modules CSS offrent une grande flexibilité car ils sont parfaits pour définir le CSS et transmettre des styles.

Si vous ne savez pas ce que sont les modules CSS, vous pouvez consulter ce lien. créez également le fichier layout.module.css. La classe .layout sera une grille avec deux colonnes et trois lignes. La première colonne avec la valeur de 18rem est spécifiquement pour la barre latérale. Les lignes 80px sont respectivement pour l'en-tête et le pied de page.

src/layout/layout.module.css

.layout {
  display[19659021]: grid;
  grid-template-columns: 18rem 1fr;
  grid-template-rows : 80px 1fr 80px;
  min-height: 100vh ;
}

.header {
  grid-area: 1 / 1 / 2  / 3;
}

.aside {
  grid-area: 2 / 1  / 4 / 2;
}

.main {
  grid-area: 2  / 2 / 3 / 3;
}

.footer {
  grid-area: 3 / 2 / 4 / 3;
}

Si vous souhaitez en savoir plus sur la grille CSS, vous devriez consulter ce guide complet et le jeu CSS Grid Garden.

Ensuite, nous devons mettre à jour le App.jsx pour utiliser le composant Layout que nous venons de créer et ajouter quelques routes.

import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Layout from './ layout/Layout.jsx';
import Home from './views/home/Home.jsx';[19659022]import Profile from './views/profile/Profile.jsx';
import Settings from './views/settings /Settings.jsx';

import './App.css';

function App() {
   return (
    <Router>
      <div className="App"[19659021]>
        <Layout>
          <Switch>
            <Route exact chemin=" /">
              <Accueil />
            </Itinéraire>
            <Route[19659032]chemin="/profile">
              <Profil />
            </Route[19659021]>
            <Route chemin=[19659021]"/paramètres">
              <Paramètres />
            </Itinéraire>
          </ Switch>
        </Layout>
      </div>
    </Routeur>
   );
}

export default Application;

Nous avons trois routes pour les composants HomeProfile et Settings. Nous avons besoin d'au moins quelques routes, car nous voulons pouvoir basculer entre différentes pages lorsque nous avons terminé avec le menu latéral récursif. Ensuite, créez ces trois composants.

src/views/home/Home.jsx

import React from 'react';

 const Accueil = accessoires => {
  return <div>Page d'accueil</div>;
};

export default Accueil;

src/views/profile/Profile.jsx

import React from 'react';

const Profile = accessoires => {
  retour <div>Page de profil</div>[19659021];
};

export default Profil;

src/views/settings/Settings.jsx

import React from 'react';
import { Switch, Route, useRouteMatch } de 'react-router-dom';
import Sécurité de[19659020]'./views/Security';
const Settings = props => {
  let { chemin } = useRouteMatch();
  return (
    <div>
      <Switch>
        <Route path={`${path}/ account`}>Compte</Route>
        <Route chemin=[19659021]{`[19659307]${chemin}/sécurité`}>
          <Sécurité />[19659189]</Route>
      </Switch>
    </div>
  );
}[19659021];

export paramètres par défaut;
Les composants 

Home et Profile n'ont rien d'autre qu'un peu de texte. Cependant, dans le composant Settingsnous avons deux routes imbriquées : compte et sécurité. La première route ne restitue que du texte, mais la seconde restitue un composant Security.

Avec cette configuration, nous avons ces 5 routes :

  • /
  • /profile
  • /settings/account[19659340]/settings/security/credentials
  • /settings/security/2fa

Créons maintenant le menu récursif.

Commençons par installer heroicons en exécutant npm install @heroicons/react , ou yarn add @heroicons/react. Les icônes sont un excellent moyen d'améliorer l'apparence visuelle d'un menu de navigation de la barre latérale.

Ensuite, nous devons créer des fichiers de configuration de menu et de barre latérale. Nous allons exporter une constante sideMenu qui sera un tableau d'objets. Chaque objet peut contenir ces propriétés :

  • label – L'étiquette de texte affichée pour le lien
  • Icon – Le composant Icône affiché à côté de l'étiquette
  • to – Le chemin du composant NavLink du routeur
  • children – Un tableau imbriqué de liens

Si un objet a la propriété children , alors il est traité comme un en-tête de navigation. Il aura une icône en chevron pour ouvrir et fermer les liens imbriqués. Si aucun children n'est spécifié, ce sera un lien de navigation.

src/layout/components/sidebar/menu.config.js

import { 
  AccueilIcône,
  UserIcône,
  Icône Cog,
  UserCircleIcon,
  ShieldCheckIcon,
  LockOpenIcon,
  DeviceMobileIcon,
} from '@heroicons/react/outline';

export const sideMenu = [[19659362]{
    étiquette: 'Maison',
    Icône : AccueilIcône,
    à: '/',
  },
  {
    étiquette: 'Profil',
    Icône : Icône utilisateur,
    à: '/profil',
  },
  {
    étiquette: 'Paramètres',
    Icône : CogIcon,
    à: '/paramètres',
    enfants : [
      {
        étiquette: 'Compte',
        Icône : UserCircleIcon,
        à: 'compte',
      },
      {
        étiquette: 'Sécurité',
        Icône : ShieldCheckIcon,
        à: 'sécurité',
        enfants: [
          {
            étiquette: 'Identifiants',
            Icône : LockOpenIcon,
            à : 'identifiants',
          },
          {
            étiquette: '2-FA',
            Icône : DeviceMobileIcon,
            à: '2fa',
          },
        ],
      },
    ],
   },
];

Une fois la configuration du menu prête, l'étape suivante consiste à créer un composant de barre latérale qui contiendra le menu récursif.

src/layout/components/sidebar/Sidebar.jsx

 import React from 'react';
import style from './sidebar.module.css';
 import NavItem from './navItem/NavItem.jsx';
import { sideMenu } from '. /menu.config.js';

const Sidebar = props => {
  return (
    <nav className={style.sidebar}>
      {sideMenu.map((item, index) => {[19659452]return <NavItem key={`${item.label}[19659246]-${index}`} item={ article} />;
      })}
    </nav>
  )[19659021];
};

export default Barre latérale;

Le composant de barre latérale parcourt le tableau de configuration sideMenu que nous avons spécifié auparavant et affiche le composant NavItem pour chaque élément. Le composant NavItem reçoit un objet item comme accessoire. Nous allons arriver au composant NavItem dans un instant. Nous devons également créer un fichier CSS pour la barre latérale.

src/layout/components/sidebar/sidebar.module.css

.sidebar {
  background-color[19659021]: #1e40af;
  hauteur: 100%;
}

Nous devons mettre à jour le composant Layout pour inclure le composant Sidebar que nous venons de créer. Importez-le et affichez-le dans l'élément aside comme indiqué ci-dessous.

src/layout/Layout.jsx

import React from  'react';
import style from './layout.module.css';
import Sidebar from  './components/sidebar/Sidebar.jsx';

const Layout = props => {
  const { enfants } = accessoires;

  return (
    <div className={style .layout}>
      <header className={style. header}></header>
      <aside className={ style.side}>
        <Sidebar />
      </side>
      < main className={style.main}>{enfants }</main>
      <footer className={style.footer}></footer>
    </div>
  );
}[19659021];

export default Layout;

Super ! Nous pouvons ensuite nous concentrer sur le composant NavItem. Le composant NavItem vérifiera si le passage d'objet item contient la propriété children. Si c'est le cas, il renverra un composant NavItemHeader. Cependant, s'il n'y a pas de liens children imbriqués, le NavItem rendra le composant NavLink de la bibliothèque react-router-dom .

Notez que nous utilisons le composant NavLink au lieu de l'habituel Link. La raison en est que le composant NavLink nous permet de spécifier activeClassNamequi est utilisé pour changer la couleur d'arrière-plan du lien actuellement actif.

src/layout /components/sidebar/navItem/NavItem.jsx

import React from 'react';
import { NavLink  } de 'react-router-dom';
import style from './navItem.module.css';
 import NavItemHeader de './NavItemHeader.jsx';

console.log({ style });
const NavItem = accessoires => {
  const { étiquette, Icône, à, enfants }[19659055]= accessoires.item;

  if (children) {
    return <NavItemHeader  article={accessoires.article} />;
  }

   return (
    <NavLink
      exact
      to={to}
      className={style.navItem}
      activeClassName={style.activeNavItem}
    >
      <Icon className={style.navIcon} />[19659067]span className={style.navLabel}>{label}</span>
    </NavLink>
  );
};[19659025]export default NavItem;

Le dernier composant que nous devons créer est le composant NavItemHeader. Ce composant est responsable du rendu conditionnel des liens imbriqués. Il affiche toujours un bouton avec une icône et une étiquette spécifiées dans la configuration ainsi que l'icône chevron. En plus de cela, il parcourt le tableau children. Si un élément du tableau children possède également une propriété childrenun autre composant NavItemHeader est rendu. Sinon, le composant NavLink est rendu.

src/layout/components/sidebar/navItem/NavItemHeader.jsx

import React,[19659029]{ useState } from 'react';
import { NavLink, useLocation }[19659041]de 'react-router-dom';
import style from './navItem.module.css';
import[19659029]{ ChevronDownIcon } from '@heroicons/react/outline';

const resolveLinkPath = (child , parentTo) => `${parentTo}/${childTo}`;

const NavItemHeader = accessoires = > {
  const { élément } = accessoires;
  const { label, Icône, à: headerToPath, enfants } = élément;
  const location = useLocation();

  const [expanded, setExpand] =[19659026]useState(
    emplacement.chemin.includes(headerToPath)
  );

  const onExpandChange = et => {
    e.preventDefault();
    setExpand(expanded => !expanded) ;
  };
	
  retour (
    <>
      <bouton
        className={`${style.navItem} ${style.navItemHeaderButton}`}
        onClick={onExpandChange}
      >
        <Icon className={style.navIcon} />
        <span className={style.navLabel}>{label}</span>
        <ChevronDownIcône
          className={`${style.navItemHeaderChevron} ${
            élargi && style.chevronÉtendu
          }`}
        />
      </bouton>

      { étendu && (
        <div className={style.navChildrenBlock}>
          {enfants.carte((item, index) => {[19659751]const clé = `${item.label}-${index}`;

            const { label, Icône, enfants } = article;

            if (enfants) {
              retour (
                <div clé={clé}>
                  <NavItemHeader
                    item={{
                      ...item,
                      à : résolution LinkPath(élément.à, accessoires.élément.à),
                    }}
                  />
                </div>
              );
            }

            retour (
              <NavLink
                clé={clé}
                à={resolveLinkPath(item.to, accessoires.item.à)}
                className={style.navItem}
                activeClassName={style.activeNavItem}
              >
                <Icône className={style.navIcon} />
                <span className={style.navLabel}>{ label}</span>
              </NavLink>
            );
          })}
        </div>
      )}
    </>
  );
};

export default NavItemHeader;

Enfin, voici les classes qui sont partagées entre les composants NavItem et NavItemHeader.

src/layout/components/sidebar/navItem/navItem.module. css

.navItem {
  padding: 0.8rem 1.25rem;
  text-decoration : none;
  display: flex;
  align-items: center;
}

 .navItem:hover {
  background-color: #1e3a8a;
}

.activeNavItem {[19659110]color: #dbeafe;
  background-color: #1e3a8a;
}

.navIcon  {
  couleur: #d1d5db;
  largeur: 1.5rem;
  hauteur: 1,5rem;
  marge-droite: 1rem;
}[19659122].navLabel {
  color: #d1d5db;
  font-size: 1rem;[19659039]}

.navItemHeaderButton {
  width: 100%;
  outline: aucun;[19659110]border: aucun;
  background: transparent;
  cursor: pointeur;
} 

.navItemHeaderChevron {
  color: #d1d5db;
  width: 1.5rem; 19659110]hauteur: 1,5rem;
  marge-gauche: auto;
  transition: tous 0.25s;
}

.chevronExpanded {
  transform: rotate(180 deg);
}

.navChildrenBlock {
  background-color: hsl(226 , 71%36%);
}

Après avoir ajouté ces styles, vous devriez voir le menu latéral récursif affiché dans le gif au début de ce didacticiel.

C'est tout. J'espère que vous avez trouvé ce tutoriel utile et que vous avez une meilleure idée de la façon d'implémenter un menu récursif dans React. Vous pouvez utiliser ce code dans vos propres projets et le développer. Les composants rendus récursivement peuvent être un peu intimidants à première vue, mais il est bon de savoir comment les implémenter, car ils peuvent être très utiles, en particulier dans des scénarios comme celui que nous venons de couvrir. Vous pouvez trouver l'exemple de code complet pour ce didacticiel dans ce référentiel GitHub.




Source link