Kaherecode

Crée ton composant Tabs avec React

Hey salut, bienvenue dans ce tutoriel sur React. Nous allons ensemble créer un composant React pour afficher un module d'onglets sur nos pages web. Tu peux déjà voir la version finale de ce que nous allons créer sur ce CodeSandbox.

Les onglets vont nous permettre d'afficher différents contenus sur une page web et permettre à nos utilisateurs de naviguer entre ce contenu en cliquant sur des onglets.

Nous allons dans notre cas créer deux composants:

Je vais dans ce tutoriel utiliser CodeSandbox pour ne pas avoir à installer un nouveau projet React sur mon ordinateur. Je vais créer un fichier tabs.js dans lequel je définirai les deux composants Tab et Tabs, et ensuite importer ces composants dans le composant App. Je vais commencer par créer le composant Tab, qui doit afficher un seul onglet avec son contenu. C'est parti.

// tabs.js

import React from "react";
import PropTypes from "prop-types";

export const Tab = ({ title }) => (
  <li className="tab-item">{title}</li>
);

Tab.propTypes = {
  title: PropTypes.string.isRequired
};

C'est tout pour l'instant, notre composant va juste prendre un paramètre title et retourner un élément HTML <li> avec le paramètre title.

Nous allons maintenant créer le composant Tabs, toujours dans le même fichier:

// tabs.js

import React from "react";
import PropTypes from "prop-types";

export const Tab = ({ title }) => (
  <li className="tab-item">{title}</li>
);

export default function Tabs() {
  return (
    <div className="tabs">
      <ul className="tab-list">
        {/* Render a list of Tab component */}
      </ul>

      <div className="tab-content">
        {/* Render the content of a Tab if it's active */}
      </div>
    </div>
  );
}

Tab.propTypes = {
  title: PropTypes.string.isRequired
};

La structure du composant Tabs est plutôt simple aussi, nous affichons une liste de composant Tab dans un élément HTML <ul>, puis le contenu de l'onglet actif dans la balise <div> juste en dessous, nous allons tout de suite voir comment mettre tout cela en pratique, mais avant, nous allons importer nos composants dans le composant App et créer un module à onglet:

// App.js

import React from "react";

import Tabs from "./Tabs";

export default function App() {
  return (
    <>
      <h1>Hello I'm Aliou Diallo!</h1>

      <Tabs>
        <div title="about">
          <h3>About me</h3>
          <p>
            I'm Mamadou Aliou Diallo a.k.a alioukahere. A Web Developer (Python,
            PHP, JS, …Web), Technical Writer, Passionate about
            entrepreneurship, writing and teaching code. Currently working on
            Kaherecode (<a href="https://www.kaherecode.com">kaherecode.com</a>
            ), an aspiring community for french developers, a web platform to
            learn and share about programming.
          </p>
        </div>
        <div title="experiences">
          <h3>My experiences</h3>
          <ul>
            <li>
              <strong>Web Full Stack Developer - Kewel (2019 - Present)</strong>
              <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
                porta, libero nec maximus varius, sapien lorem aliquet ex, quis
                faucibus odio lorem in quam.
              </p>
            </li>
            <li>
              <strong>
                Web Full Stack Developer - Qualshore (2017 - 2019)
              </strong>
              <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
                porta, libero nec maximus varius, sapien lorem aliquet ex, quis
                faucibus odio lorem in quam.
              </p>
            </li>
          </ul>
        </div>
        <div title="contact">
          <h3>Get in touch</h3>
          <p>
            <strong>Mail</strong>:{" "}
            <a href="mailto:aliou.diallo@kaherecode.com">
              aliou.diallo@kaherecode.com
            </a>{" "}
            <br />
            <strong>Adress</strong>:Dakar, Senegal
          </p>
        </div>
      </Tabs>
    </>
  );
}

Et voilà, le composant Tabs contient 3 autres blocs div qui sont les différents onglets et leur contenu.

Pour l'instant, rien n'est afficher sur la page à part l'élément h1. Nous allons commencer par afficher juste les onglets, sans le contenu. Dans le fichier tabs.js:

// tabs.js

import React from "react";
import PropTypes from "prop-types";

export const Tab = ({ title }) => <li className="tab-item">{title}</li>;

export default function Tabs({ children }) {
  return (
    <div className="tabs">
      <ul className="tab-list">
        {children.map(tab => {
          const { title } = tab.props;

          return <Tab key={title} title={title} />;
        })}
      </ul>

      <div className="tab-content">
        {/* Render the content of a Tab if it's active */}
      </div>
    </div>
  );
}

Tab.propTypes = {
  title: PropTypes.string.isRequired
};

Tabs.propTypes = {
  children: PropTypes.instanceOf(Array).isRequired
};

Je modifie le composant Tabs pour parcourir le tableau children et à chaque fois je retourne un nouveau composant Tab.

Nous avons maintenant un début de notre composant d'onglets qui s'affiche comme sur l'image suivante:

Nous allons juste apporter un peu de CSS, je vais utiliser styled-jsx pour celà:

// tabs.js

import React from "react";
import PropTypes from "prop-types";

export const Tab = ({ title }) => (
  <>
    <li className="tab-item">{title}</li>

    <style jsx="true">{`
      li.tab-item {
        list-style-type: none;
        padding: 1rem 2rem;
        background-color: #b2beb5;
        font-weight: bold;
        text-transform: uppercase;
        letter-spacing: 0.1rem;
        cursor: pointer;
        transition: all 0.5s ease;
      }

      li.tab-item:hover {
        background-color: #76fa97;
      }
    `}</style>
  </>
);

export default function Tabs({ children }) {
  return (
    <>
      <div className="tabs">
        <ul className="tab-list">
          {children.map(tab => {
            const { title } = tab.props;

            return <Tab key={title} title={title} />;
          })}
        </ul>

        <div className="tab-content">
          {/* Render the content of a Tab if it's active */}
        </div>
      </div>

      <style jsx="true">{`
        .tab-list {
          padding: 0;
          display: flex;
        }
      `}</style>
    </>
  );
}

Tab.propTypes = {
  title: PropTypes.string.isRequired
};

Tabs.propTypes = {
  children: PropTypes.instanceOf(Array).isRequired
};

Notre composant commence à prendre forme.

Mais on est encore loin du compte, il nous faut maintenant afficher le contenu de nos onglets, nous allons aussi utiliser la fonction map de javascript pour parcourir le tableau children du composant Tabs et afficher le contenu de chaque élément:

// tabs.js

import React from "react";
import PropTypes from "prop-types";

export const Tab = ({ title }) => (
  <>
    <li className="tab-item">{title}</li>

    <style jsx="true">{`
      li.tab-item {
        list-style-type: none;
        padding: 1rem 2rem;
        background-color: #b2beb5;
        font-weight: bold;
        text-transform: uppercase;
        letter-spacing: 0.1rem;
        cursor: pointer;
        transition: all 0.5s ease;
      }

      li.tab-item:hover {
        background-color: #76fa97;
      }
    `}</style>
  </>
);

export default function Tabs({ children }) {
  return (
    <>
      <div className="tabs">
        <ul className="tab-list">
          {children.map(tab => {
            const { title } = tab.props;

            return <Tab key={title} title={title} />;
          })}
        </ul>

        <div className="tab-content">
          {/* Render the content of a Tab if it's active */}
          {children.map(tab => tab.props.children)}
        </div>
      </div>

      <style jsx="true">{`
        .tab-list {
          padding: 0;
          display: flex;
        }
      `}</style>
    </>
  );
}

Tab.propTypes = {
  title: PropTypes.string.isRequired
};

Tabs.propTypes = {
  children: PropTypes.instanceOf(Array).isRequired
};

J'ai juste rajouter la ligne {children.map(tab => tab.props.children)} qui va parcourir tous les enfants du composant Tabs puis afficher leurs enfants à eux (complex tout ça), et nous avons quelque chose comme cela:

Ce n'est pas ce que nous voulons.

Si tu lis le commentaire juste avant, il est dit qu'il faut afficher le contenu de l'onglet qui est actif, mais pour l'instant nous ne pouvons pas savoir quel onglet est actif.

Tu te rappelles, au début on disait que le composant Tabs devrait servir a afficher la liste de composant Tab, mais aussi à maintenir l'état pour savoir quel onglet est actif. Nous allons donc utiliser useState de React pour créer un état activeTab. Cet état contiendra le title de l'onglet qui est actif. Nous allons par défaut l'initialiser avec le premier onglet, et ensuite nous afficherons le contenu d'un onglet que si son title équivaut au state activeTab:

// tabs.js

import React, { useState } from "react";
import PropTypes from "prop-types";

export const Tab = ({ title }) => (
  <>
    <li className="tab-item">{title}</li>

    <style jsx="true">{`
      li.tab-item {
        list-style-type: none;
        padding: 1rem 2rem;
        background-color: #b2beb5;
        font-weight: bold;
        text-transform: uppercase;
        letter-spacing: 0.1rem;
        cursor: pointer;
        transition: all 0.5s ease;
      }

      li.tab-item:hover {
        background-color: #76fa97;
      }
    `}</style>
  </>
);

export default function Tabs({ children }) {
  const [activeTab, setActiveTab] = useState(children[0].props.title);

  return (
    <>
      <div className="tabs">
        <ul className="tab-list">
          {children.map(tab => {
            const { title } = tab.props;

            return <Tab key={title} title={title} />;
          })}
        </ul>

        <div className="tab-content">
          {children.map(tab => {
            if (tab.props.title !== activeTab) return undefined;

            return tab.props.children;
          })}
        </div>
      </div>

      <style jsx="true">{`
        .tab-list {
          padding: 0;
          display: flex;
        }

        .tab-content {
          padding: 0 1rem;
        }

        .tab-content p {
          text-align: justify;
        }
      `}</style>
    </>
  );
}

Tab.propTypes = {
  title: PropTypes.string.isRequired
};

Tabs.propTypes = {
  children: PropTypes.instanceOf(Array).isRequired
};

Et boom, nous avons juste le contenu de l'onglet About qui est afficher.

Mais nous ne pouvons pas changer d'onglet pour l'instant.

Pour remedier à cela, nous allons créer une méthode onClickTabItem qui va modifier le state activeTab du composant, puis nous enverrons cette méthode à notre composant Tab qui va écouter l'évènement onClick:

// tabs.js

import React, { useState } from "react";
import PropTypes from "prop-types";

export const Tab = ({ title, onClick, active = false }) => {
  const onClickTab = e => {
    e.preventDefault(0);
    onClick(title);
  };

  return (
    <>
      <li className={`${active ? "active" : ""} tab-item`} onClick={onClickTab}>
        {title}
      </li>

      <style jsx="true">{`
        li.tab-item {
          list-style-type: none;
          padding: 1rem 2rem;
          background-color: #b2beb5;
          font-weight: bold;
          text-transform: uppercase;
          letter-spacing: 0.1rem;
          cursor: pointer;
          transition: all 0.5s ease;
        }

        li.tab-item:hover,
        li.tab-item.active {
          background-color: #76fa97;
        }
      `}</style>
    </>
  );
};

export default function Tabs({ children }) {
  const [activeTab, setActiveTab] = useState(children[0].props.title);

  const onClickTabItem = tab => setActiveTab(tab);

  return (
    <>
      <div className="tabs">
        <ul className="tab-list">
          {children.map(tab => {
            const { title } = tab.props;

            return (
              <Tab
                key={title}
                title={title}
                onClick={onClickTabItem}
                active={title === activeTab ? true : false}
              />
            );
          })}
        </ul>

        <div className="tab-content">
          {children.map(tab => {
            if (tab.props.title !== activeTab) return undefined;

            return tab.props.children;
          })}
        </div>
      </div>

      <style jsx="true">{`
        .tab-list {
          padding: 0;
          display: flex;
        }

        .tab-content {
          padding: 0 1rem;
        }

        .tab-content p {
          text-align: justify;
        }
      `}</style>
    </>
  );
}

Tab.propTypes = {
  title: PropTypes.string.isRequired,
  onClick: PropTypes.func.isRequired
};

Tabs.propTypes = {
  children: PropTypes.instanceOf(Array).isRequired
};

Et voilà, notre composant d'onglets est maintenant fonctionnel, tu peux changer d'onglets comme tu le souhaites et nous avons même un statut actif ou pas pour changer la couleur de fond de l'onglet actif.

Si tu veux utiliser ce composant, tu prends juste le contenu du fichier tabs.js, tu modifies le style CSS à tes besoins et tu mets le contenu de tes onglets dans un composant Tabs comme nous l'avons fait dans le fichier App.js. Le code source est disponible sur ce CodeSandbox.

Merci d'avoir suivi ce tutoriel, si tu as des questions ou remarques n'hésite pas à laisser un commentaire ci-dessous ou à me joindre sur le chat Discord de Kaherecode ou je serais plus apte à te répondre le plus vite possible.


Merci à

Mamadou Aliou Diallo

Mamadou Aliou Diallo

@alioukahere

Développeur web fullstack avec une passion pour l’entrepreneuriat et les nouvelles technologies. Fondateur de Kaherecode.

Continue de lire