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.
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.layoutsera une grille avec deux colonnes et trois lignes. La première colonne avec la valeur de18remest spécifiquement pour la barre latérale. Les lignes80pxsont 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.jsxpour utiliser le composantLayoutque 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
HomeProfileetSettings. 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
HomeetProfilen'ont rien d'autre qu'un peu de texte. Cependant, dans le composantSettingsnous 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
sideMenuqui 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 composantNavLinkdu 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 aucunchildrenn'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
sideMenuque nous avons spécifié auparavant et affiche le composantNavItempour chaque élément. Le composantNavItemreçoit un objetitemcomme accessoire. Nous allons arriver au composantNavItemdans 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
Layoutpour inclure le composantSidebarque nous venons de créer. Importez-le et affichez-le dans l'élémentasidecomme 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 composantNavItemvérifiera si le passage d'objetitemcontient la propriétéchildren. Si c'est le cas, il renverra un composantNavItemHeader. Cependant, s'il n'y a pas de lienschildrenimbriqués, leNavItemrendra le composantNavLinkde la bibliothèquereact-router-dom.Notez que nous utilisons le composant
NavLinkau lieu de l'habituelLink. La raison en est que le composantNavLinknous permet de spécifieractiveClassNamequi 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 tableauchildrenpossède également une propriétéchildrenun autre composantNavItemHeaderest rendu. Sinon, le composantNavLinkest 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
NavItemetNavItemHeader.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

