Implementing Dependent Dropdowns in React with API Filtering

When building forms in React, you might need to create dependent dropdowns, where the options in one dropdown depend on the selection of another. This is commonly used in scenarios like selecting a category and then filtering relevant subcategories.

In this article, we’ll cover:

  1. How to create a dependent dropdown feature in React.
  2. How to filter options dynamically using an API.
  3. Handling empty states when no options are available.

1. Setting Up the React Component

Let’s start by setting up a simple form component with two dropdowns: Category and Subcategory.

import React, { useState, useEffect } from “react”;

const DependentDropdown = () => {

 const [categories, setCategories] = useState([]);

 const [subcategories, setSubcategories] = useState([]);

 const [selectedCategory, setSelectedCategory] = useState(“”);

 const [formData, setFormData] = useState({ subcategory_name: “”, subcategory_id: null });

 useEffect(() => {

  // Fetch categories from API on component mount

  fetch(“https://api.example.com/categories”)

   .then((response) => response.json())

   .then((data) => setCategories(data))

   .catch((error) => console.error(“Error fetching categories:”, error));

 }, []);

 useEffect(() => {

  if (selectedCategory) {

   // Fetch subcategories based on selected category

   fetch(`https://api.example.com/subcategories?categoryId=${selectedCategory}`)

    .then((response) => response.json())

    .then((data) => setSubcategories(data))

    .catch((error) => console.error(“Error fetching subcategories:”, error));

  } else {

   setSubcategories([]);

  }

 }, [selectedCategory]);

 return (

  <div>

   {/* Category Dropdown */}

   <div>

    <label>Category:</label>

    <select

     value={selectedCategory}

     onChange={(e) => setSelectedCategory(e.target.value)}

    >

     <option value=””>Select Category</option>

     {categories.map((category) => (

      <option key={category.id} value={category.id}>

       {category.name}

      </option>

     ))}

    </select>

   </div>

   {/* Subcategory Dropdown */}

   <div>

    <label>Subcategory:</label>

    <select

     value={formData.subcategory_name}

     onChange={(e) => {

      const selectedSubcategoryId = e.target.options[e.target.selectedIndex].dataset.id;

      setFormData({

       …formData,

       subcategory_name: e.target.value,

       subcategory_id: selectedSubcategoryId ? parseInt(selectedSubcategoryId) : 0,

      });

     }}

    >

     <option value=””>Select Subcategory</option>

     {subcategories.length > 0 ? (

      subcategories.map((subcategory) => (

       <option key={subcategory.id} data-id={subcategory.id} value={subcategory.name}>

        {subcategory.name}

       </option>

      ))

     ) : (

      <option disabled>No options available</option>

     )}

    </select>

   </div>

  </div>

 );

};

export default DependentDropdown;

2. Explanation of the Code

Fetching Categories

  • On component mount, the useEffect hook fetches category data from the API and updates the categories state.
  • The category dropdown is populated with options from the API response.

Fetching and Filtering Subcategories

  • When a category is selected, a second useEffect hook fetches the corresponding subcategories from the API based on the categoryId.
  • If no category is selected, the subcategories list is cleared.

Handling Empty State

  • If the subcategories array is empty, we display a disabled <option>No options available</option>.
  • This ensures a better user experience by informing users when no data is available.

3. Benefits of This Approach

Dynamic Filtering: Fetches only the necessary data when needed, reducing API calls.

Scalability: Can be expanded to more dropdowns, like City → State → Country.

Improved UX: Avoids showing irrelevant data and gracefully handles empty states.

This approach is useful in e-commerce platforms, admin dashboards, and any form where selection dependencies exist.

Leave a comment

Your email address will not be published. Required fields are marked *