في العديد من تطبيقات الويب، غالبًا ما نواجه نماذج حيث يؤدي اختيار خيار في قائمة منسدلة واحدة إلى فتح مجموعة جديدة من الخيارات في أخرى. هذه القوائم المنسدلة المترابطة، المعروفة عادةً بالقوائم المنسدلة المعتمدة أو المتسلسلة، تلعب دورًا حاسمًا في خلق تجربة ملء نموذج سلسة وبديهية.

سواء كان ذلك اختيار دولة للكشف عن الولايات المقابلة أو اختيار فئة منتج لعرض عناصر محددة، فإن هذه القوائم المنسدلة تبسط الخيارات المعقدة للجميع. بالنسبة للمطورين، فإن تنفيذ القوائم المنسدلة المعتمدة يمثل تحديًا عمليًا يجمع بين المنطق، وسهولة الاستخدام، والتعامل مع البيانات الديناميكية.

في هذا الدليل، ستتعلم كيفية تنفيذ هذا النوع من القوائم المنسدلة في تطبيق 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' },
  ];

  // بيانات المدن الثابتة المت Corresponding لدول معينة
  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 إلى سلسلة فارغة.

  • إذا كان البلد المحدد ليس ‘آخر’، يتم إعادة تعيين حالة otherCountry إلى سلسلة فارغة. هذا يضمن أنه إذا كان المستخدم قد قام سابقًا بكتابة شيء في مدخل “آخر”، يتم مسح هذا النص بمجرد اختيارهم بلدًا مختلفًا (على سبيل المثال، “الولايات المتحدة” أو “كندا”).

بالنسبة لاختيار البلد ‘آخر’، نحتاج فقط إلى تتبع القيمة المُدخلة في المدخل. تقوم وظيفة 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> ديناميكيًا لكل مدينة في المصفوفة.

  • يتم تعيين key لكل خيار إلى index ويتم تعيين value إلى city.

  • يتم عرض كل مدينة كنص للخيار.

هذا كل ما علينا القيام به للحصول على قائمة منسدلة تعتمد على البيانات الثابتة لدينا.

إليك كل الكود مجمعًا:

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 ووضعه داخل قسم الإرجاع لمكون التطبيق.

import DependentDropdown from './DependentDropdown'

function App() {

  return (
    <DependentDropdown/>
  )
}

export default App

لا تنسَ تشغيل التطبيق بإدخال أي من هذه الأوامر:

npm start // لإنشاء تطبيق رياكت
npm run dev // لتطبيق رياكت فيت

أخيرًا، هذا ما يجب أن يظهر في المتصفح لديك:

التعامل مع البيانات الديناميكية (طلبات API)

في التطبيقات الواقعية، قد لا تكون القوائم المنسدلة ثابتة. بدلاً من ذلك، قد يتم جلبها من API أو ملف JSON يعمل كـ API.

في هذا المثال، سنقوم بقراءة البيانات من ملف JSON لملء القائمة المنسدلة المعتمدة لدينا. هذه الممارسة لها بعض الفوائد وهي:

  • تقليل حمل قاعدة البيانات: من خلال استخدام ملف JSON ثابت (أو ملف محمّل مسبقًا)، فإنك تقلل من عدد الاستعلامات إلى قاعدة البيانات التي ستكون مطلوبة خلاف ذلك لملء القوائم المنسدلة. هذا مفيد بشكل خاص إذا كانت خيارات القائمة المنسدلة ثابتة إلى حد ما ولا تتغير كثيرًا.

  • تسريع عرض واجهة المستخدم: نظرًا لأن البيانات موجودة بالفعل على جانب العميل، فلا حاجة لطلب ذهاب وإياب إلى الخادم في كل مرة يتفاعل فيها المستخدم مع القائمة المنسدلة. هذا يمكن أن يجعل الواجهة تبدو أكثر استجابة.

يحتوي ملف JSON الخاص بنا على الولايات والمناطق الحكومية المحلية، التي تعادل الدول والمدن.

يتم تمثيل البيانات في ملف JSON كمصفوفة من الكائنات، مع وجود مفتاح لكل كائن لـ الولاية، الاسم البديل، و المناطق الحكومية المحلية. يحتوي مفتاح ‘المناطق الحكومية المحلية’ على مصفوفة.

إليك كيف يتم تمثيلها:

[
  {
    "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 لمزيد من المقالات والنشرات المتعلقة بالبرمجة.

أراك في المقالة القادمة!