React drop down data binding
(If you want to use a drop down in a function component with hooks, check out this post)
Drop down data binding is always interesting in different UI technologies. First of all feeding the drop down a list of dynamic data values from a web service and then saving the selected value in our state along with some simple validation. So, let’s give this a go in react …
We’re going to build a react drop down component that asks the user their favourite premiership football team. We already have a web API that gives us the premiership football teams at http://localhost:26854/api/premiershipteams
.
Drop down items
Let’s start off with a basic component that has a teams
array in our state ready to hold the premiership teams. We have the componentDidMount
life cycle method ready to make the web service call to get the teams. We also have our select
html tag ready to be populated with a collection of items.
class FavouriteTeam extends Component {
state = {
teams: []
};
componentDidMount() {}
render() {
return (
<div>
<select></select>
</div>
);
}
}
We’ll implement the web service call first using fetch
. The data is returned from the server as a simple list of strings. i.e. ["Arsenal","Bournemouth","Brighton and Hove Albion", ...]
.
We use array.map
to produce a list of objects that have a value
property (for the drop down value) and a display
property (for the text to be displayed in the drop down). i.e. [{"value": "Arsenal", "display": "Arsenal"}, ...]
.
We add a blank item at the start of the array using array.concat
that prompts the user to choose a team. The concatenated array is then set in the teams array in the component state.
componentDidMount() {
fetch("http://localhost:26854/api/premiershipteams")
.then((response) => {
return response.json();
})
.then(data => {
let teamsFromApi = data.map(team => {
return {value: team, display: team}
});
this.setState({
teams: [{value: '', display: '(Select your favourite team)'}].concat(teamsFromApi)
});
}).catch(error => {
console.log(error);
});
}
Now, that we have the teams in the component state, we can map each team to a option
tag not forgetting to construct a key
attribute to keep react happy.
render() {
return (
<div>
<select>
{this.state.teams.map((team) => <option key={team.value} value={team.value}>{team.display}</option>)}
</select>
</div>
)
}
Selected item
That’s a great start. Next up is to store the selected item in the component state. So, let’s initialise the state property to be the blank item:
state = {
teams: [],
selectedTeam: ""};
We then need to control the value of the select
tag and also set the selectedTeam
state property when the user selects a team:
render() {
return (
<div>
<select value={this.state.selectedTeam} onChange={(e) => this.setState({selectedTeam: e.target.value})}> {this.state.teams.map((team) => <option key={team.value} value={team.value}>{team.display}</option>)}
</select>
</div>
)
}
Validation
Lastly, let’s add some validation to prevent the user from selecting the blank team which is the first item in the teams
array. So, let’s introduce some state for the validation error:
state = {
teams: [],
selectedTeam: "",
validationError: ""};
We’ll set the validationError
state to “You must select your favourite team” if the selected value is a blank string. We’ll also show the validationError
state in red text:
render() {
return (
<div>
<select
value={this.state.selectedTeam}
onChange={e => this.setState({selectedTeam: e.target.value,validationError: e.target.value === "" ? "You must select your favourite team" : ""})} >
{this.state.teams.map((team) => <option key={team.value} value={team.value}>{team.display}</option>)}
</select>
</div>
<div style={{color: 'red', marginTop: '5px'}}> {this.state.validationError} </div> )
}
Conclusion
That’s it! Implementing a drop down actually feels fairly natural in react. Here’s the final component:
class FavouriteTeam extends Component {
state = {
teams: [],
selectedTeam: "",
validationError: ""
};
componentDidMount() {
fetch(
"http://localhost:26854/api/premiershipteams"
)
.then(response => {
return response.json();
})
.then(data => {
let teamsFromApi = data.map(team => {
return { value: team, display: team };
});
this.setState({
teams: [
{
value: "",
display:
"(Select your favourite team)"
}
].concat(teamsFromApi)
});
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<div>
<select
value={this.state.selectedTeam}
onChange={e =>
this.setState({
selectedTeam: e.target.value,
validationError:
e.target.value === ""
? "You must select your favourite team"
: ""
})
}
>
{this.state.teams.map(team => (
<option
key={team.value}
value={team.value}
>
{team.display}
</option>
))}
</select>
<div
style={{
color: "red",
marginTop: "5px"
}}
>
{this.state.validationError}
</div>
</div>
);
}
}
Comments
Steve AD October 6, 2018
thank you very much, it helps a lot
Marina October 23, 2018
This amazing tutorial! Many tks!
Susman October 29, 2018
Thanks for your help
Deepali February 7, 2019
This was helpful, thanks!
Kiron April 17, 2019
Very helpful thanks a lot
rajashree April 18, 2019
Amazing tutorial. really helpful
Joao Felipe Neves Coelho June 19, 2019
Thank you, it helped a lot, I’m newbie on ReactJs, but I’m lovin’ it.
Shivani October 21, 2019
I’m trying to fetch specific keyword (name) from the big JSON response through an API.
I followed your approach, but it does not work for me! Is that in fetch we need to pass headers and method information too?
componentDidMount() {
fetch(“http://127.0.0.1:3400/sck_page/get_name”)
.then((res) => {
return res.json();
})
.then(data => {
let namesFromAPI = data.map(name => { return { value: name, display: name } })
this.setState({ names: [{ value: ”, display: ‘(Select the site)’ }].concat(namesFromAPI) });
})
.catch(err => {
console.log(err);
});
}
Carl October 22, 2019
Hi Shivani,
Thanks for the question. There is a 2nd parameter in fetch where we can pass these:
fetch(“http://127.0.0.1:3400/sck_page/get_name”, {method: “POST”, headers: {‘Content-Type’: ‘application/json’}})
Srdjan October 31, 2019
First, good article 🙂 Second, what is the best practice to implement multiple dropdowns on one page? In one response or to fetch within promises each dropdown’s options?
Carl October 31, 2019
Thanks Srdjan. In terms of fetching data from multiple dropdowns, it depends … If it’s a small form and not too many dropdowns, then fetching the data for all the dropdowns in one go is nice. However, for large forms containing lots of dropdowns, I tend to load the data for them on demand … generally fetching the data they become visible in the active tab / section
Srdjan October 31, 2019
Regarding the option in one response, I am not sure that it is correct to make such a route in REST api which will return multiple resources in one response, or did you mean to cascade promises of each dropdown and to update state when the last one is fetched?
In the way with loading the date on demand should submit form button be disabled until request is done and dropdown populated? My main concern here is edit form where already saved value should be preselected, but if I go with empty dropdown at the beginning and fetch data on demand, will the correct value still be preselected?
Carl November 1, 2019
Yes, I’d definitely disable the dropdown whilst its data is being fetched.
In terms of the one response, yes, for small forms I sometimes use a single endpoint for many dropdowns:
For example /lookups/?type=title,gender,country
would give data for the title, gender and country fields.
Srdjan November 1, 2019
I totally agree with disabling dropdown. Should also submit button be disabled while data is being fetched? This could be useful especially in case that dropdown is one the required fields.
That example for fetching multiple resources is interesting, I will definitely consider it.
So, you would prefer that way in favor of cascading promises?
Ram November 6, 2019
Just what i was looking for ..!! thanks
Zoran November 20, 2019
Thanks a lot!
Rajiv C November 25, 2019
Good one, do you have mutiple dropdown databinding examples.. ?
Vis December 3, 2019
Thanks Carl. Decent explanation
I have dependent dropdowns like below, to be populated through an Array. E.g. mmvList = [apple,iPhone,v8, apple,iPhone,xr, samsung,tab,3, samsung,mobile,s10, …] Form Input:- text: DeviceName, select: Make, select: Model, select: Version Form Submit:- List Backups And 2 JS functions:
- populateMMV: To populate from local storage
- changeMMV: If user select the Make, it should populate only relevant Model and version
Could you please tell the react way to invoke those 2 functions?
Much appreciated!
monari66 December 9, 2019
Great article: clear and concise.
tpnmd December 9, 2019
I have a react-table where I need to implement the dropdown selection. Do you have an example that you can share?
Thanks very much.
If you to learn about using TypeScript with React, you may find my course useful: