Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

React drop down data binding

May 01, 2018
react

(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:

  1. populateMMV: To populate from local storage
  2. 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.

Did you find this post useful?

Let me know by sharing it on Twitter.
Click here to share this post on Twitter

If you to learn about using TypeScript with React, you may find my course useful:

Using TypeScript with React

Find out more