ביישומי אינטרנט רבים, אנחנו לעיתים קרובות נתקלים בטפסים שבהם בחירה באופציה אחת בתפריט הנפתח פותחת סט חדש של אפשרויות בתפריט אחר. תפריטים נפתחים אלה, הידועים בדרך כלל כתפריטים תלויים או תפריטים מת cascading, משחקים תפקיד קרדינלי ביצירת חוויית מילוי טופס חלקה ואינטואיטיבית.

אם זה לבחור מדינה כדי לחשוף מדינות מתאימות או לבחור קטגוריית מוצר כדי להציג פריטים ספציפיים, תפריטים אלה מפשטים בחירות מורכבות עבור כולם. עבור מפתחים, יישום תפריטים תלויים הוא אתגר מעשי שמשלב לוגיקה, שימושיות וטיפול בנתונים דינמיים.

במדריך זה, תלמד כיצד ליישם סוג זה של תפריט נפתח ביישום React שלך.

תוכן עניינים

מהו תפריט נפתח תלוי?

תפריט נפתח תלוי הוא אלמנט בממשק משתמש שבו האפשרויות הזמינות בתפריט אחד נקבעות על פי הבחירה שנעשתה בתפריט אחר. לדוגמה, נניח שיש לכם שני תפריטים:

  1. תפריט מדינה: המשתמש בוחר מדינה.

  2. תפריט עיר: בהתבסס על המדינה שנבחרה, רשימת הערים הזמינות בתפריט השני תסונן בהתאם.

סוג אינטראקציה זה הוא קרדינלי עבור טפסים שדורשים קלטים נתונים מורכבים, רגישים להקשר.

איך עובד תפריט נפתח תלוי?

תפריטים נפתחים תלויים פועלים על ידי עדכון דינמי של האפשרויות בתפריט השני בהתבסס על הערך שנבחר בתפריט הראשון. שינוי דינמי זה מתבצע בדרך כלל על ידי:

  1. האזנה לקלט המשתמש: כאשר המשתמש בוחר אפשרות בתפריט הראשון, אירוע (בדרך כלל onChange) מפעיל פונקציה לעדכון המצב.

  2. קבלת נתונים חדשים: המצב המעודכן יכול לשמש לסינון של הנתונים הקיימים או לבצע קריאה ל-API כדי לקבל את רשימת האפשרויות החדשה.

  3. עיבוד נתונים חדשים: תיבת הרשימה השנייה מתעדכנת לאחר מכן עם האפשרויות החדשות, מספקת למשתמש בחירות רלוונטיות.

שלבים ליצירת תיבות רשימה תלת-עומדיות בריאקט

שלב 1: הגדרת פרויקט ריאקט שלך

אם אתה חדש בריאקט ורוצה לעקוב, תבדוק את מסמכי Vite ותבצע את השלבים ליצירת הפרויקט שלך בריאקט. כאשר תסיים, חזור לכאן ונמשיך לבנות.

אם כבר יש לך פרויקט ריאקט שברצונך להשתמש בו, זה גם נהדר.

שלב 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] הוא undefined), ה־|| [] מבטיח שמערך ריק ([]) ישמש כפולבק וימנע שגיאות בעת ניסיון להצגת הערים.

  • כאשר המשתמש משנה את בחירת המדינה, הפונקציה 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> עבור כל אובייקט מדינה במערך.

  • שם של כל מדינה מוצג טקסט כאופציה.

  • המפתח של כל אפשרות מוגדר לזהות של המדינה והערך מוגדר לשם של המדינה.

  • המפתח עוזר ל-Reactive לנהל את הרשימה בצורה יעילה כאשר יש צורך לרנדר מחדש.

תפריט הערים מוצג באופן מותנה בהתאם למדינה שנבחרה. אם אפשרות 'אחר' נבחרה, מוצגת שדה טקסט עבור המשתמש לציין את המדינה. אחרת, אם מדינה תקפה נבחרה, מוצג תפריט ערים עם אפשרויות רלוונטיות.

{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> עבור כל עיר במערך.

  • המפתח של כל אפשרות מוגדר למספר סידורי והערך מוגדר לעיר.

  • כל עיר מוצגת כטקסט של האפשרות.

זהו כל מה שצריך לעשות כדי ליצור רשימה נפתרת תלויתית באמצעות המידע הסטטי שלנו.

הנה כל הקוד שמוצג ביחד:

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 סטטי (או קובץ שהוטען מראש), אתה מפחית את מספר השאילתות לבסיס הנתונים שיידרשו בדרך כלל כדי למלא תפריטים נפתחים. זה שימושי במיוחד אם אפשרויות התפריט הנפתח די סטטיות ואינן משתנות לעיתים קרובות.

  • הצגת UI מהירה יותר: מכיוון שהנתונים כבר בצד הלקוח, אין צורך בבקשה חזרה לשרת בכל פעם שהמשתמש אינטראקציה עם התפריט הנפתח. זה יכול לגרום לממשק להרגיש יותר תגובתי.

קובץ ה-JSON שלנו מכיל מדינות ו-LGAs (אזורים מקומיים של ממשלה), שהם המקבילים למדינות ולערים.

הנתונים בקובץ ה-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);

//איסוף נתונים באמצעות ה-hook 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} />;

}
//טופס מקבל נתונים כ-props
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;

השינוי המרכזי כאן הוא איסוף הנתונים באמצעות ה-hook useEffect, אשר אוסף את נתוני המדינות וה-LGA רק בהצגה הראשונה

הנה איך זה מוצג בדפדפן:

סיכום

במדריך זה, למדת כיצד ליצור תפריטים נפתחים תלויים ב-Reactor באמצעות נתונים סטטיים ודינמיים. עכשיו אתה יכול להשתמש בסוג זה של תפריט נפתח ביישומי React שלך.

אם מצאת את המאמר הזה מועיל, אתה יכול להתחבר אלי ב-LinkedIn למאמרים ופוסטים נוספים הקשורים לתכנות.

נתראה בפעם הבאה!