Dans de nombreuses applications web, nous rencontrons souvent des formulaires où sélectionner une option dans une liste déroulante débloque un nouvel ensemble d’options dans une autre. Ces listes déroulantes interconnectées, communément appelées listes déroulantes dépendantes ou en cascade, jouent un rôle crucial dans la création d’une expérience de remplissage de formulaire fluide et intuitive.

Que ce soit en sélectionnant un pays pour révéler les états correspondants ou en choisissant une catégorie de produit pour afficher des articles spécifiques, ces listes déroulantes simplifient les choix complexes pour tout le monde. Pour les développeurs, la mise en œuvre de listes déroulantes dépendantes est un défi pratique qui combine logique, facilité d’utilisation et gestion dynamique des données.

Dans ce tutoriel, vous apprendrez comment implémenter ce type de liste déroulante dans votre application React.

Table des matières

Qu’est-ce qu’un menu déroulant dépendant ?

Un menu déroulant dépendant est un élément d’interface utilisateur dans lequel les options disponibles dans un menu déroulant sont déterminées par la sélection effectuée dans un autre menu déroulant. Par exemple, considérons un scénario où vous avez deux menus déroulants :

  1. Menu déroulant des pays : L’utilisateur sélectionne un pays.

  2. Menu déroulant des villes : En fonction du pays sélectionné, la liste des villes disponibles dans le deuxième menu déroulant sera filtrée en conséquence.

Ce type d’interaction est crucial pour les formulaires qui nécessitent des saisies de données complexes et sensibles au contexte.

Comment fonctionne un menu déroulant dépendant ?

Les menus déroulants dépendants fonctionnent en mettant à jour dynamiquement les options du deuxième menu déroulant en fonction de la valeur sélectionnée dans le premier menu déroulant. Ce changement dynamique est généralement réalisé par :

  1. Écouter l’entrée de l’utilisateur : Lorsque l’utilisateur sélectionne une option dans le premier menu déroulant, un événement (généralement onChange) déclenche une fonction pour mettre à jour l’état.

  2. Récupération de nouvelles données: Cet état mis à jour peut être utilisé pour filtrer les données existantes ou effectuer un appel API pour récupérer la nouvelle liste d’options.

  3. Rendering de nouvelles données: Le deuxième menu déroulant est ensuite mis à jour avec les nouvelles options, offrant à l’utilisateur des choix pertinents.

Étapes pour créer des menus déroulants dépendants en React

Étape 1: Configurer votre projet React

Si vous débutez avec React et que vous souhaitez suivre, consultez la documentation de Vite et suivez les étapes pour créer votre projet React. Une fois terminé, revenez ici et continuons à construire.

Si vous avez déjà un projet React que vous souhaitez utiliser, c’est aussi très bien.

Étape 2: Structurer le composant

Pour simplifier, supposons que nous construisons un menu déroulant dépendant à deux niveaux où le premier menu déroulant vous permet de choisir un pays, et le second menu déroulant affiche les villes en fonction du pays sélectionné.

De plus, dans le menu déroulant des pays, nous aurons une autre option pour entrer un nom de pays qui n’est pas inclus dans les options de pays. L’utilisateur pourra ensuite entrer son pays dans un champ de texte.

Tout d’abord, créez un nouveau fichier nommé DependentDropdown.js ou DependentDropdown.jsx. Dans ce fichier, définissez un composant fonctionnel appelé DependentDropdown.

Nous allons maintenant suivre les étapes suivantes pour construire notre menu déroulant dépendant :

Déclarer des variables pour stocker des données

Nous devons créer des données statiques pour les valeurs de nos pays et villes :

  // Données statiques des pays
  const countries = [
    { id: 1, name: 'USA' },
    { id: 2, name: 'Canada' },
    { id: 3, name: 'Other' },
  ];

  // Données statiques des villes correspondant aux pays
  const cities = {
    USA: ['New York', 'Los Angeles', 'Chicago'],
    Canada: ['Toronto', 'Vancouver', 'Montreal'],
  };
  • countries est un tableau d’objets. Chaque objet ayant des propriétés id et name.

  • cities est un objet avec des noms de pays comme clés et les valeurs comme tableau de villes.

Déclarer des variables d’état

Pour chaque sélection de pays ou de villes, nous voulons pouvoir suivre les valeurs sélectionnées. Nous voulons également pouvoir remplir l’option des villes après qu’une sélection de pays a été faite. Pour ce faire, nous devons déclarer certains états.

Si le concept d’état est nouveau pour vous, vous pouvez lire mon article sur l’état ici.

  const [selectedCountry, setSelectedCountry] = useState('');
  const [availableCities, setAvailableCities] = useState([]);
  const [selectedCity, setSelectedCity] = useState('');
  const [otherCountry, setOtherCountry] = useState('');
  • L’état selectedCountry est déclaré et sa valeur initiale est définie sur une chaîne vide.

  • L’état availableCities est déclaré et sa valeur initiale est définie sur un tableau vide.

  • L’état selectedCity est déclaré et sa valeur initiale est définie sur une chaîne vide.

  • L’état otherCountry est déclaré et sa valeur initiale est définie sur une chaîne vide.

Gestion des Événements

Dans le processus de sélection dans le menu déroulant, nous voulons que certaines actions soient exécutées. Les gestionnaires d’événements nous permettent de le faire dans le cas d’un événement, qui dans ce cas est l’événement onChange.

  const handleCountryChange = (e) => {
    const country = e.target.value;
    setSelectedCountry(country);
    setAvailableCities(cities[country] || []);
    setSelectedCity(''); 
     if (country !== 'Other') {
      setOtherCountry('');
    }
  };

Voici ce qui se passe dans la fonction handleCountryChange :

  • Récupère la valeur de l’option sélectionnée dans le menu déroulant (le pays qui a été sélectionné).

  • Le setSelectedCountry met à jour la variable d’état (selectedCountry) avec le pays nouvellement sélectionné.

  • cities[country] recherche la liste des villes pour le pays sélectionné dans l’objet cities.

    • Si cities[country] est trouvé, il définit cette liste de villes comme les villes disponibles.

    • Si aucune ville n’est trouvée pour le pays sélectionné (cities[country] est indéfini), le || [] garantit qu’un tableau vide ([]) est utilisé comme solution de secours, empêchant les erreurs lors de l’affichage des villes.

  • Lorsque l’utilisateur modifie la sélection du pays, la fonction setSelectedCity réinitialise le selectedCity à une chaîne vide.

  • Si le pays sélectionné n’est pas « Autre », l’état otherCountry est réinitialisé à une chaîne vide. Cela garantit que si l’utilisateur avait précédemment saisi quelque chose dans le champ « Autre », ce texte est effacé une fois qu’il sélectionne un pays différent (par exemple, « USA » ou « Canada »).

Pour la sélection du pays « Autre », nous devons juste garder une trace de la valeur saisie dans le champ. La fonction setOtherCountry met à jour la valeur saisie. Et voici comment c’est fait :

  const handleOtherCountryChange = (e) => {
    setOtherCountry(e.target.value);
  };

Pour le changement de villes, nous n’avons pas besoin de faire grand-chose car le pays sélectionné détermine quelles villes sont affichées. Tout ce que nous devons faire est de mettre à jour le selectedCity avec la valeur de l’option sélectionnée dans la liste déroulante, qui est la ville sélectionnée.

Dans React, la fonction de mise à jour s’occupe de la mise à jour des variables d’état, donc le setSelectedCity gère cela dans ce cas.

La fonction handleCityChange sera :

  const handleCityChange = (e) => {
    setSelectedCity(e.target.value);
  };

Retourner du JSX

Le composant DependentDropdown rend trois éléments principaux : le menu déroulant des pays, le menu déroulant des villes et le champ de texte pour le pays.

Un menu déroulant en HTML est une combinaison des éléments <select> et <option>. Pour suivre la valeur des éléments, nous allons attacher des variables d’état à ceux-ci afin de pouvoir les contrôler. Faire cela est appelé ‘Contrôler les éléments’, tandis que les éléments eux-mêmes sont appelés ‘Éléments contrôlés’ dans React.

Pour contrôler l’élément <select> du pays, nous allons lui donner un attribut value de selectedCountry et également attacher la fonction handleCountryChange à celui-ci.

     <label htmlFor="country" className='font-bold'>Select Country: </label>
      <select id="country" value={selectedCountry} onChange={handleCountryChange}>
        <option value="">Select a country</option>
        {countries.map((country) => (
          <option key={country.id} value={country.name}>
            {country.name}
          </option>
        ))}
      </select>

De plus,

  • À l’intérieur du <option>, nous parcourons le tableau countries et créons dynamiquement un <option> pour chaque objet pays dans le tableau.

  • Le name de chaque pays est affiché comme le texte de l’option.

  • La key de chaque option est définie sur l’id du pays et la value est définie sur le name du pays.

  • La key aide React à gérer la liste de manière efficace lors du re-rendu.

Le menu déroulant des villes est rendu conditionnellement en fonction du pays sélectionné. Si l’option de pays ‘Autre’ est choisie, un champ de saisie de texte est affiché pour que l’utilisateur précise le pays. Sinon, si un pays valide est sélectionné, un menu déroulant des villes avec des options pertinentes est affiché.

{selectedCountry === 'Other' ? (
        <>
          <label htmlFor="other-country" className='font-bold'>Please specify the country: </label>
          <input
            id="other-country"
            type="text"
            value={otherCountry}
            onChange={handleOtherCountryChange}
            placeholder="Enter country name"
          />
        </>
      ) : (
        selectedCountry && (
          <>
            <label htmlFor="city" className='font-bold'>Select City: </label>
            <select id="city" value={selectedCity} onChange={handleCityChange}>
              <option value="">Select a city</option>
              {availableCities.map((city, index) => (
                <option key={index} value={city}>
                  {city}
                </option>
              ))}
            </select>
          </>
        )
      )
}

De plus :

  • Nous vérifions si selectedCountry est l’option ‘Autre’ et affichons un champ de saisie de texte.

  • Le champ de saisie de texte a un état otherCountry et la fonction gestionnaire handleOtherCountryChange qui lui est attachée.

  • Nous contrôlons l’élément <select> des villes en utilisant l’attribut value, en le définissant sur la variable d’état de selectedCity. Le gestionnaire d’événements, handleCityChange, est également attaché pour gérer les événements onChange.

  • Nous parcourons le tableau availableCities et créons dynamiquement un <option> pour chaque ville du tableau.

  • La clé de chaque option est définie sur un index et la valeur est définie sur la city.

  • Chaque ville est affichée en tant que texte de l’option.

C’est tout ce que nous devons faire pour avoir un menu déroulant dépendant fonctionnel en utilisant nos données statiques.

Voici tout le code rassemblé :

import React, { useState } from 'react';

const DependentDropdown = () => {
  // Données de pays statiques
  const countries = [
    { id: 1, name: 'USA' },
    { id: 2, name: 'Canada' },
    { id: 3, name: 'Other' },
  ];

  // Données de ville statiques correspondant aux pays
  const cities = {
    USA: ['New York', 'Los Angeles', 'Chicago'],
    Canada: ['Toronto', 'Vancouver', 'Montreal'],
  };

  // État pour conserver le pays, la ville sélectionnée et tout autre texte de pays
  const [selectedCountry, setSelectedCountry] = useState('');
  const [availableCities, setAvailableCities] = useState([]);
  const [selectedCity, setSelectedCity] = useState('');
  const [otherCountry, setOtherCountry] = useState(''); 

  // Gérer le changement de pays
  const handleCountryChange = (e) => {
    const country = e.target.value;
    setSelectedCountry(country);
    setAvailableCities(cities[country] || []);
    setSelectedCity(''); 
    if (country !== 'Other') {
      setOtherCountry('');
    }
  };

  // Gérer le changement de ville
  const handleCityChange = (e) => {
    setSelectedCity(e.target.value);
  };

  // Gérer le changement de saisie d'un autre pays
  const handleOtherCountryChange = (e) => {
    setOtherCountry(e.target.value);
  };

  return (
    <div className='text-center text-3xl'>
      <h1 className='font-extrabold text-5xl p-10'>Dependent Dropdown Example</h1>

      {/* Menu déroulant de pays */}
      <label htmlFor="country" className='font-bold'>Select Country: </label>
      <select id="country" value={selectedCountry} onChange={handleCountryChange}>
        <option value="">Select a country</option>
        {countries.map((country) => (
          <option key={country.id} value={country.name}>
            {country.name}
          </option>
        ))}
      </select>

      {/* Ville ou saisie d'un autre pays */}
      {selectedCountry === 'Other' ? (
        <>
          <label htmlFor="other-country" className='font-bold'>Please specify the country: </label>
          <input
            id="other-country"
            type="text"
            value={otherCountry}
            onChange={handleOtherCountryChange}
            placeholder="Enter country name"
          />
        </>
      ) : (
        selectedCountry && (
          <>
            <label htmlFor="city" className='font-bold'>Select City: </label>
            <select id="city" value={selectedCity} onChange={handleCityChange}>
              <option value="">Select a city</option>
              {availableCities.map((city, index) => (
                <option key={index} value={city}>
                  {city}
                </option>
              ))}
            </select>
          </>
        )
      )}
    </div>
  );
};

export default DependentDropdown;

Étape 3 : Utiliser le composant

Pour obtenir vos résultats finaux, vous devez importer le composant DependentDropdown dans votre App.js ou App.jsx et le placer à l’intérieur de la section de retour du composant App.

import DependentDropdown from './DependentDropdown'

function App() {

  return (
    <DependentDropdown/>
  )
}

export default App

N’oubliez pas de lancer l’application en entrant l’une de ces commandes :

npm start //pour créer une application react
npm run dev //pour une application react vite

Enfin, voici ce qui devrait s’afficher dans votre navigateur :

Gestion des données dynamiques (requêtes API)

Dans les applications du monde réel, les listes des menus déroulants ne sont peut-être pas statiques. Au lieu de cela, elles peuvent être récupérées à partir d’une API ou d’un fichier JSON agissant comme une API.

Dans cet exemple, nous lirons des données à partir d’un fichier JSON pour peupler notre menu déroulant dépendant. Cette pratique présente certains avantages qui sont :

  • Charge de base de données réduite: En utilisant un fichier JSON statique (ou un fichier préchargé), vous réduisez le nombre de requêtes à la base de données qui seraient sinon nécessaires pour remplir les menus déroulants. C’est particulièrement utile si les options de menu déroulant sont assez statiques et ne changent pas souvent.

  • Rendu plus rapide de l’IU: Puisque les données sont déjà côté client, il n’est pas nécessaire d’effectuer une requête aller-retour vers le serveur à chaque interaction de l’utilisateur avec le menu déroulant. Cela peut rendre l’interface plus réactive.

Notre fichier JSON contient des états et des LGAs (Local Government Areas), qui sont l’équivalent de pays et de villes.

Les données dans le fichier JSON sont représentées sous forme d’un tableau d’objets, chaque objet ayant des clés pour état, alias et lgas. La clé ‘lgas’ contient un tableau.

Voici comment cela est représenté:

[
  {
    "state": "Adamawa",
    "alias": "adamawa",
    "lgas": [
      "Demsa",
      "Fufure",
      "Toungo",
      "Yola North",
      "Yola South"
    ]
  },
  {
    "state": "Akwa Ibom",
    "alias": "akwa_ibom",
    "lgas": [
      "Abak",
      "Uruan",
      "Urue-Offong/Oruko",
      "Uyo"
    ]
  },
//le reste des objets
]

Cette méthode de création d’un menu déroulant dynamique dépendant à partir d’une API n’est pas trop différente de l’exemple précédent, à l’exception de quelques modifications mineures.

Voici comment nous avons récupéré et utilisé des données à partir d’un fichier JSON :

import React, { useEffect, useState } from "react";

function DependentDropdown() {
//déclaration des variables d'état globales
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

//récupération des données en utilisant le hook useEffect
  useEffect(() => {
    fetch("nigeria-state-and-lgas.json") //fichier JSON défini comme URL
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      })
      .catch((error) => {
        console.error("Error fetching data:", error);
        setLoading(false);
      });
  }, []);
  return loading ? <div>Loading...</div> : <Form data={data} />;

}
//formulaire recevant des données en tant que props
function Form({ data }) {

//déclaration des variables d'état locales
  const [selectedState, setSelectedState] = useState("");
  const [selectedLga, setSelectedLga] = useState("");
  const [showList, setShowList] = useState(false);
  let sortedData = data.slice().sort((a, b) => a.state.localeCompare(b.state));
  const selectedData = sortedData.find((item) => item.state === selectedState);

//fonction de gestion pour l'état
  function handleClickState(e) {
    setSelectedState(e.target.value);
    setShowList(true);
  }
//fonction de gestion pour Lga
  function handleClickLga(e) {
    setSelectedLga(e.target.value);
  }

  return (
    <div>
  <form onSubmit={handleFormSubmit}>
    <div>
      {/* Prénom */}
      <div>
        <label htmlFor="firstName">First Name</label>
        <input type="text"
          id="firstName"
          name="firstName"
          placeholder="Enter your first name"/>
      </div>

      {/* Nom de famille */}
      <div>
        <label htmlFor="lastName">
          Last Name
        </label>
        <input
          type="text"
          id="lastName"
          name="lastName"
          placeholder="Enter your last name"/>
      </div>
    </div>

    <div>
      <div>
        <select value={selectedState} onChange={handleClickState} name="state">
          <option value="" disabled>Choose your state</option>
          {sortedData.map((data) => (
            <option key={data.alias} value={data.state}>
              {data.state}
            </option>
          ))}
        </select>
      </div>
      {selectedData && showList && (
        <select value={selectedLga} onChange={handleClickLga} name="lga">
          <option value="" disabled>{`Choose your LGA in ${selectedState}`}</option>
          {selectedData.lgas.map((lgass) => (
            <option key={lgass} value={lgass}>
              {lgass}
            </option>
          ))}
        </select>
      )}

    </div>
    <div>
        <button type="submit">
          Submit
        </button>
      </div>
  </form>
</div>
  );
}

export default DependentDropdown;

La modification clé ici est la récupération des données en utilisant le useEffect hook, qui récupère les données d’état et LGA uniquement lors du premier rendu

Voici comment cela s’affiche dans le navigateur :

Conclusion

Dans ce tutoriel, vous avez appris comment créer des menus déroulants dépendants en React en utilisant à la fois des données statiques et dynamiques. Vous pouvez maintenant utiliser ce type de menu déroulant dans vos applications React.

Si vous avez trouvé cet article utile, vous pouvez me contacter sur LinkedIn pour plus d’articles et publications liés à la programmation.

A bientôt !