Во многих веб-приложениях мы часто сталкиваемся с формами, где выбор опции в одном выпадающем списке открывает новый набор опций в другом. Эти взаимосвязанные выпадающие списки, обычно известные как зависимые или каскадные выпадающие списки, играют ключевую роль в создании гладкого и интуитивно понятного опыта заполнения формы.

Будь то выбор страны для отображения соответствующих штатов или выбор категории товара для отображения конкретных товаров, эти выпадающие списки упрощают сложные выборы для всех. Для разработчиков реализация зависимых выпадающих списков является практическим вызовом, который объединяет логику, удобство использования и динамическую обработку данных.

В этом учебнике вы узнаете, как реализовать этот тип выпадающего списка в вашем приложении React.

Содержание

Что такое зависимый выпадающий список?

Зависимый выпадающий список – это элемент пользовательского интерфейса, в котором доступные варианты в одном выпадающем списке определяются выбором, сделанным в другом выпадающем списке. Например, рассмотрим ситуацию, где у вас есть два выпадающих списка:

  1. Страна: Пользователь выбирает страну.

  2. Город: Основываясь на выбранной стране, список доступных городов во втором выпадающем списке будет соответственно фильтроваться.

Такое взаимодействие крайне важно для форм, требующих ввода сложных данных, зависящих от контекста.

Как работает зависимый выпадающий список?

Зависимые выпадающие списки работают путем динамического обновления вариантов во втором выпадающем списке на основе выбранного значения в первом выпадающем списке. Это динамическое изменение обычно достигается путем:

  1. Отслеживания пользовательского ввода: Когда пользователь выбирает вариант в первом выпадающем списке, событие (обычно onChange) запускает функцию для обновления состояния.

  2. Получение новых данных: Это обновленное состояние можно использовать для фильтрации существующих данных или для выполнения API-запроса, чтобы получить новый список вариантов.

  3. Отрисовка новых данных: Второй выпадающий список обновляется новыми вариантами, предоставляя пользователю актуальные выборы.

Шаги для создания зависимых выпадающих списков в React

Шаг 1: Настройте свой проект React

Если вы новичок в React и хотите следовать за нами, ознакомьтесь с документацией Vite и выполните шаги для создания вашего проекта React. Когда закончите, вернитесь сюда и давайте продолжим строить.

Если у вас уже есть проект React, который вы хотите использовать, это тоже отлично.

Шаг 2: Структурируйте компонент

Для простоты предположим, что мы создаем двухуровневый зависимый выпадающий список, где первый выпадающий список позволяет выбрать страну, а второй выпадающий список отображает города в зависимости от выбранной страны.

Также в выпадающем списке стран у нас будет еще один вариант для ввода названия страны, которое не включено в список стран. Пользователь затем может продолжить вводить свою страну в текстовом поле.

Сначала создайте новый файл с именем DependentDropdown.js или DependentDropdown.jsx. Внутри этого файла определите функциональный компонент с именем DependentDropdown.

Теперь мы пройдем следующие шаги для построения нашего зависимого выпадающего списка:

Объявите переменные для хранения данных

Нам нужно создать статические данные для значений наших стран и городов:

  // Статические данные о странах
  const countries = [
    { id: 1, name: 'USA' },
    { id: 2, name: 'Canada' },
    { id: 3, name: 'Other' },
  ];

  // Статические данные о городах, соответствующих странам
  const cities = {
    USA: ['New York', 'Los Angeles', 'Chicago'],
    Canada: ['Toronto', 'Vancouver', 'Montreal'],
  };
  • countries – это массив объектов. Каждый объект имеет свойства id и name.

  • cities – это объект с названиями стран в качестве ключей и значениями в виде массива городов.

Объявите переменные состояния

Для каждого выбора страны или города мы хотим отслеживать выбранные значения. Мы также хотим заполнить список городов после выбора страны. Для этого нам нужно объявить некоторые состояния.

Если концепция состояния нова для вас, вы можете прочитать мою статью о состоянии здесь.

  const [selectedCountry, setSelectedCountry] = useState('');
  const [availableCities, setAvailableCities] = useState([]);
  const [selectedCity, setSelectedCity] = useState('');
  const [otherCountry, setOtherCountry] = useState('');
  • Состояние selectedCountry объявлено, и его начальное значение установлено в пустую строку.

  • Состояние availableCities объявлено, и его начальное значение установлено в пустой массив.

  • Состояние selectedCity объявлено, и его начальное значение установлено в пустую строку.

  • Состояние otherCountry объявлено, и его начальное значение установлено в пустую строку.

Обработка событий

В процессе выбора в выпадающем списке мы хотим, чтобы некоторые действия выполнялись. Обработчики событий позволяют нам это сделать в случае события, которым в данном случае является событие onChange.

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

Вот что происходит в функции handleCountryChange:

  • Получает значение выбранного варианта в выпадающем списке (выбранная страна).

  • setSelectedCountry обновляет переменную состояния (selectedCountry) ново выбранной страной.

  • cities[country] ищет список городов для выбранной страны в объекте cities.

    • Если cities[country] найдены, устанавливается этот список городов в качестве доступных городов.

    • Если города не найдены для выбранной страны (cities[country] неопределен), || [] гарантирует, что используется пустой массив ([]) в качестве запасного варианта, предотвращая ошибки при попытке отображения городов.

  • Когда пользователь изменяет выбор страны, функция setSelectedCity сбрасывает selectedCity до пустой строки.

  • Если выбранная страна не является “Other”, состояние otherCountry сбрасывается до пустой строки. Это гарантирует, что если пользователь ранее что-то ввел в поле “Other”, то текст будет очищен после выбора другой страны (например, “USA” или “Canada”).

Для выбора страны “Other” нам просто нужно отслеживать значение, введенное в поле. Функция setOtherCountry обновляет введенное значение. И вот как это делается:

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

Для изменения городов нам не нужно делать много, потому что выбранная страна определяет отображаемые города. Все, что нам нужно сделать, это обновить selectedCity до значения выбранной опции в выпадающем списке, которое является выбранным городом.

В React функция обновления выполняет обновление переменных состояния, поэтому setSelectedCity обрабатывает это в данном случае.

Функция handleCityChange будет:

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

Возвращение JSX

Компонент DependentDropdown отображает три основных элемента: выпадающий список стран, выпадающий список городов и текстовое поле страны.

Выпадающий список в HTML представляет собой комбинацию элементов <select> и <option>. Чтобы отслеживать значение элементов, мы присвоим им переменные состояния, чтобы иметь возможность их контролировать. Это называется ‘Управление элементами’, а сами элементы в React называются ‘Управляемыми элементами’.

Чтобы управлять элементом <select> для выбора страны, мы присвоим ему атрибут value равный selectedCountry и также прикрепим к нему функцию handleCountryChange.

     <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>

Также,

  • Внутри элемента <option> мы отображаем массив countries и динамически создаем элемент <option> для каждого объекта страны в массиве.

  • Имя каждой страны отображается как текст опции.

  • Ключ key для каждого варианта устанавливается в id страны, а value устанавливается в name страны.

  • Ключ key помогает React эффективно управлять списком при повторном рендеринге.

Выпадающий список городов отображается в зависимости от выбранной страны. Если выбрана опция «Другая страна», отображается текстовое поле для ввода, чтобы пользователь мог указать страну. В противном случае, если выбрана действительная страна, отображается выпадающий список городов с соответствующими вариантами.

{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>
          </>
        )
      )
}

Кроме того:

  • Мы проверяем, является ли selectedCountry опцией «Другая» и отображаем текстовое поле.

  • Текстовое поле имеет состояние otherCountry и функцию-обработчик handleOtherCountryChange, прикрепленную к нему.

  • Мы управляем элементом <select> для городов, используя атрибут value, устанавливая его в переменную состояния selectedCity. Обработчик событий handleCityChange также прикреплен для обработки событий onChange.

  • Мы проходим по массиву availableCities и динамически создаем элемент <option> для каждого города в массиве.

  • Каждому элементу option присваивается key, равный index, и value, равный city.

  • Каждый город отображается как текст элемента option.

Это все, что нам нужно сделать, чтобы создать функциональный зависимый выпадающий список, используя наши статические данные.

Вот весь код, собранный вместе:

import React, { useState } from 'react';

const DependentDropdown = () => {
  // Статические данные стран
  const countries = [
    { id: 1, name: 'USA' },
    { id: 2, name: 'Canada' },
    { id: 3, name: 'Other' },
  ];

  // Статические данные городов, соответствующие странам
  const cities = {
    USA: ['New York', 'Los Angeles', 'Chicago'],
    Canada: ['Toronto', 'Vancouver', 'Montreal'],
  };

  // Состояние для хранения выбранной страны, города и другого текста страны
  const [selectedCountry, setSelectedCountry] = useState('');
  const [availableCities, setAvailableCities] = useState([]);
  const [selectedCity, setSelectedCity] = useState('');
  const [otherCountry, setOtherCountry] = useState(''); 

  // Обработка изменения страны
  const handleCountryChange = (e) => {
    const country = e.target.value;
    setSelectedCountry(country);
    setAvailableCities(cities[country] || []);
    setSelectedCity(''); 
    if (country !== 'Other') {
      setOtherCountry('');
    }
  };

  // Обработка изменения города
  const handleCityChange = (e) => {
    setSelectedCity(e.target.value);
  };

  // Обработка изменения ввода другой страны
  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>

      {/* Выпадающий список стран */}
      <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>

      {/* Ввод города или другой страны */}
      {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;

Шаг 3: Используйте компонент

Чтобы получить окончательные результаты, вам нужно импортировать компонент DependentDropdown в ваш App.js или App.jsx и разместить его внутри секции возврата компонента App.

import DependentDropdown from './DependentDropdown'

function App() {

  return (
    <DependentDropdown/>
  )
}

export default App

Не забудьте запустить приложение, введя одну из этих команд:

npm start // для create react app
npm run dev // для react vite app

Наконец, вот что должно отображаться в вашем браузере:

Обработка динамических данных (API запросы)

В реальных приложениях списки для выпадающих списков могут быть не статическими. Вместо этого они могут быть получены из API или JSON файла, работающего как API.

В этом примере мы будем считывать данные из JSON файла для заполнения нашего зависимого выпадающего списка. Эта практика имеет некоторые преимущества, которые заключаются в:

  • Снижение нагрузки на базу данных: Используя статический файл JSON (или предварительно загруженный файл), вы сокращаете количество запросов к базе данных, которые в противном случае потребовались бы для заполнения выпадающих списков. Это особенно полезно, если варианты выпадающего списка достаточно статичны и не часто изменяются.

  • Более быстрое рендеринг пользовательского интерфейса: Поскольку данные уже находятся на стороне клиента, нет необходимости в обращении к серверу при каждом взаимодействии пользователя с выпадающим списком. Это может сделать интерфейс более отзывчивым.

Наш файл JSON содержит данные о штатах и районах местного самоуправления (Local Government Areas), которые соответствуют странам и городам.

Данные в файле JSON представлены в виде массива объектов, каждый объект имеет ключи для state, alias и lgas. Ключ ‘lgas’ содержит массив.

Вот как это представлено:

[
  {
    "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"
    ]
  },
//остальные объекты
]

Этот метод создания динамического зависимого выпадающего списка из API не слишком отличается от предыдущего примера, за исключением некоторых незначительных модификаций.

Вот как мы извлекали и использовали данные из JSON-файла:

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

function DependentDropdown() {
//объявление глобальных переменных состояния
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

//извлечение данных с помощью хука useEffect
  useEffect(() => {
    fetch("nigeria-state-and-lgas.json") //JSON-файл задан как 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} />;

}
//форма получает данные как свойства
function Form({ data }) {

//объявление локальных переменных состояния
  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);

//функция-обработчик для состояния
  function handleClickState(e) {
    setSelectedState(e.target.value);
    setShowList(true);
  }
//функция-обработчик для LGA
  function handleClickLga(e) {
    setSelectedLga(e.target.value);
  }

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

      {/* Фамилия */}
      <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;

Основное изменение здесь – извлечение данных с использованием хука useEffect, который извлекает данные о состояниях и LGA только при первоначальной загрузке

Вот как это отображается в браузере:

Вывод

В этом руководстве вы узнали, как создавать зависимые выпадающие списки в React с использованием как статических, так и динамических данных. Теперь вы можете использовать этот тип выпадающего списка в ваших приложениях React.

Если вам была полезна эта статья, вы можете связаться со мной на LinkedIn для получения дополнительных статей и публикаций по программированию.

Увидимся в следующей!