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.
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 de18rem
est spécifiquement pour la barre latérale. Les lignes80px
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 composantLayout
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
Home
Profile
etSettings
. 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
etProfile
n'ont rien d'autre qu'un peu de texte. Cependant, dans le composantSettings
nous avons deux routes imbriquées : compte et sécurité. La première route ne restitue que du texte, mais la seconde restitue un composantSecurity
.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
, ouyarn 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 lienIcon
– Le composant Icône affiché à côté de l'étiquetteto
– Le chemin du composantNavLink
du routeurchildren
– Un tableau imbriqué de liensSi 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 aucunchildren
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 composantNavItem
pour chaque élément. Le composantNavItem
reçoit un objetitem
comme accessoire. Nous allons arriver au composantNavItem
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 composantSidebar
que nous venons de créer. Importez-le et affichez-le dans l'élémentaside
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 composantNavItem
vérifiera si le passage d'objetitem
contient la propriétéchildren
. Si c'est le cas, il renverra un composantNavItemHeader
. Cependant, s'il n'y a pas de lienschildren
imbriqués, leNavItem
rendra le composantNavLink
de la bibliothèquereact-router-dom
.Notez que nous utilisons le composant
NavLink
au lieu de l'habituelLink
. La raison en est que le composantNavLink
nous permet de spécifieractiveClassName
qui 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 tableauchildren
. Si un élément du tableauchildren
possède également une propriétéchildren
un autre composantNavItemHeader
est rendu. Sinon, le composantNavLink
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
etNavItemHeader
.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