How to Build A Dependent Select Box With ReactJS
Not too long ago, I built an HTML form with two select input fields and the options available in the second select input were to adapt and depend on the choice made in the first select input. So I decided to share how I built it.
It's called a dependent select Box, though it may also be referred to as a cascading dropdown or a dynamic dropdown. This feature is vital in web form development, as it enhances user experience and is also employed when precision and customization are required in form submissions.
Note: The full demo is at the end of this post.
Prerequisite:
For the tutorial, I assume you have
· Knowledge of HTML, CSS, and Modern Javascript
· Knowledge of React.js and React hooks
Getting Started
Set up a new React project or use your existing one.
For this tutorial, let’s set up a new React project using Vite with a React template. Follow these steps:
Create a New Vite Project:
Open your terminal and run the following command.
Replace
<project name>
with your desired project name:npm create vite@latest <project name> -- --template react
2. Navigate to the project directory using your terminal:
cd <project name>
3. Install dependencies
npm install
3. Start Your React App:
npm run dev
This command will provide you with a URL where you can view and interact with your project.
Step 1: Understand the structure of your data:
To begin our journey into building the Dependent Select Box, it’s crucial to gain a deep understanding of the structure of the data that will power our dependent dropdowns as this will help you determine how you would design and implement your component.
In this tutorial, we’ll focus on creating our data for simplicity, but if you’re working with data you are not creating yourself, it’s equally important to study and adapt your component to match the data format.
In our case, the data structure will be an array of objects. Each object within this array represents a superOption
, and it also contains an array of subOptions
.
Let’s illustrate this with an example: Imagine you’re dealing with continents as the superOptions
, and for each continent, there’s a list of countries nested within it as subOptions
.
const data = [
{
superOption: "Africa",
subOptions: ["Nigeria", "Kenya", "South Africa"],
},
{
superOption: "Asia",
subOptions: ["India", "China", "Japan"],
},
// ... add more continents and countries
];
This hierarchical structure allows us to logically organize our data, mirroring real-world scenarios where one selection depends on another.
Step 2: Create the select input UI:
With a clear understanding of our data structure, it’s time to build the user interface (UI) that interacts with this data.
Let’s create a React functional component in the src
folder that features two controlled select inputs. Additionally, we’ll implement a state variable called countries
to store a list of countries associated with the selected continent.
We’ve conveniently placed our data array at the top of the component file for clarity and easy reference.
SelectForm.tsx
// Import useState from React.
import { useState} from "react";
//Define the data structure
const data = [
{
superOption: "Africa",
subOptions: ["Nigeria", "Kenya", "South Africa"],
},
{
superOption: "Asia",
subOptions: ["India", "China", "Japan"],
},
// ... add more continents and countries
];
function SelectForm() {
// Set up your states for continent and country selection
const [selectedContinent, setSelectedContinent] = useState("");
const [selectedCountry, setSelectedCountry] = useState("");
// Create a state to store the countries
const [countries, setCountries] = useState([]);
// Create the form with select inputs
return (
<form>
<div>
<label htmlFor="continentSelect">Select Continent:</label>
<select
id="continentSelect"
name="continent"
value={selectedContinent}
onChange={(e) => setSelectedContinent(e.target.value)}
>
<option value="">Select Continent</option>
{data.map((continent) => (
<option key={continent.superOption} value={continent.superOption}>
{continent.superOption}
</option>
))}
</select>
</div>
<div>
<label htmlFor="countrySelect">Select Country:</label>
<select
id="countrySelect"
name="country"
value={selectedCountry}
onChange={(e) => setSelectedCountry(e.target.value)}
disabled={countries.length === 0}
>
<option value="">Select Country</option>
{countries.map((country) => (
<option key={country} value={country}>
{country}
</option>
))}
</select>
</div>
</form>
);
}
export default SelectForm;
Here’s an explanation of each step:
We import
useState
from React.We set up state variables
selectedContinent
andselectedCountry
to control the selected values for continent and country. They are initialized with empty stringsWe create a state variable
countries
to store the list of countries for the selected continent. It’s initialized as an empty array.Inside the return statement, we create the form with two select inputs for continent and country. We control the select inputs by setting the
value
andonChange
attributes to manage the selected values.For the first select input (continent), we loop through the
data
array usingmap()
to generate the options dynamically.For the second select input (country), we also use
map()
to generate the options based on thecountries
array.We set the
disabled
attribute to prevent selection when there are no countries available in thecountries
array, this is to prevent users from trying to select a country when no continent has been selected.
Step 3: Add a function to handle retrieving the data:
We have to retrieve the object corresponding to the selected continent and then get the subOptions
of that object. To do this, we will need to iterate through the data
array and find the object that matches the selected superOption
value (the continent), There are several approaches to achieve this, but in this tutorial, we’ll use the find()
array method.
The find ()
method iterates through an array and returns the first element in the provided array that satisfies the provided testing function. This is appropriate because our data does not have any repeated superOption
values.
const retrieveCountries = () => {
//Check if No Continent is Selected
if (selectedContinent === "") {
setCountries([]); // Reset countries when no continent is selected
} else {
// Find the Selected Continent Object
const selectedContinentObject = data.find(
(continentObj) => continentObj.superOption === selectedContinent //testing function
);
//Update Countries Based on Selected Continent
if (selectedContinentObject) {
const countriesOfSelectedContinent = selectedContinentObject.subOptions;
setCountries(countriesOfSelectedContinent);
//Reset Selected Country when the continent changes
setSelectedCountry("");
}
}
};
We’ve added the retrieveCountries
function, which is responsible for fetching the countries based on the selected continent.
Here’s an explanation of each step:
we begin by checking if any continent is selected (
selectedContinent === ""
). If no continent is selected, it resets thecountries
state to an empty array, effectively clearing the country dropdown.If a continent is selected, we Find the selected continent Object in the
data
array. It uses thefind
method, which iterates through the data array and looks for the first object that has asuperOption
value matching the selected continent value, then the result is stored in theselectedContinentObject
variable.If a matching continent object is found, it extracts the
subOptions
(countries) of that continent object and assigns them to thecountriesOfSelectedContinent
variable. It then updates thecountries
state with thesesubOptions
, effectively populating the country dropdown with the countries of the selected continent.Finally, it resets the
selectedCountry
state to an empty string. This step ensures that if a user has previously selected a country from another continent and then changes the continent, the selected country is cleared to prevent any mismatch.
Step 4: Call the retrieveCountries
function when a continent is selected
Finally, we add an onClick
event handler to the continent select input to trigger the retrieveCountries
function when the select box is clicked.
This ensures that the dependent select box behaviour is activated when the user interacts with the continent dropdown.
<div>
<label htmlFor="continentSelect">Select Continent:</label>
<select
id="continentSelect"
name="continent"
value={selectedContinent}
onChange={(e) => {
setSelectedContinent(e.target.value);
}}
onClick={retrieveCountries} // Call the function when select is clicked
>
<option value="">Select Continent</option>
{data.map((continent) => (
<option key={continent.superOption} value={continent.superOption}>
{continent.superOption}
</option>
))}
</select>
</div>
Now, the countries
state will be dynamically updated based on the selected continent, creating a cascading or dependent select box behaviour.
Full demo
import { useState } from "react";
const data = [
{
superOption: "Africa",
subOptions: ["Nigeria", "Kenya", "South Africa"],
},
{
superOption: "Asia",
subOptions: ["India", "China", "Japan"],
},
// ... add more continents and countries
];
function SelectForm() {
const [selectedContinent, setSelectedContinent] = useState("");
const [selectedCountry, setSelectedCountry] = useState("");
const [countries, setCountries] = useState([]);
const retrieveCountries = () => {
if (selectedContinent === "") {
setCountries([]);
} else {
const selectedContinentObject = data.find(
(continentObj) => continentObj.superOption === selectedContinent //testing function
);
if (selectedContinentObject) {
const countriesOfSelectedContinent = selectedContinentObject.subOptions;
setCountries(countriesOfSelectedContinent);
setSelectedCountry("");
}
}
};
return (
<div>
<h1>Select a Continent and a Country</h1>
<form>
<div>
<label htmlFor="continentSelect">Select Continent:</label>
<select
id="continentSelect"
name="continent"
value={selectedContinent}
onChange={(e) => {
setSelectedContinent(e.target.value);
}}
onClick={retrieveCountries}
>
<option value="">Select Continent</option>
{data.map((continent) => (
<option key={continent.superOption} value={continent.superOption}>
{continent.superOption}
</option>
))}
</select>
</div>
<div>
<label htmlFor="countrySelect">Select Country:</label>
<select
id="countrySelect"
name="country"
value={selectedCountry}
onChange={(e) => setSelectedCountry(e.target.value)}
disabled={countries.length === 0}
>
<option value="">Select Country</option>
{countries.map((country) => (
<option key={country} value={country}>
{country}
</option>
))}
</select>
</div>
</form>
</div>
);
}
export default SelectForm;
Subscribe to my newsletter
Read articles from Stephanie Egbuonu directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by