Lucas AllenGo to homepage

How To Use And Build A Dropdown Element In React

Dropdowns are a common user interface for many applications. They offer the user a set range of options to choose from, usually in place of more free-form text input. From another point of view, they are also useful for restricting what a user can enter into a form. If you’re building a React application and find yourself in this position, then read on!

For the purpose of brevity, let’s stick to React as much as possible, focusing less on how to generally style or build a semantic dropdown element and more on how this UI is generally used with React. For styling, a dropdown element with a select tag, see this CSS-Tricks article. Likewise, for a little more flexibility, see how 24-ways.org uses a combination of an input and a ul tag while keeping to modern accessibility standards!

React App Set-Up

You can execute the commands below to install a new React project. We’ll stick to yarn for this tutorial, but feel free to use NPM if you prefer.

yarn create react-app react-select-tutorial

Access the project folder:

cd react-select-tutorial

Execute the React project.

yarn start

Prepare App.js

We will use some static data for the dropdown’s options and pass it to our two examples via component props. Please feel free to use your own if you’re following along.

Let's update the App.js file to the following:

// App.js
import SelectOnDemand from "./components/SelectOnDemand";
import SelectLiveUpdate from "./components/SelectLiveUpdate";
import "./App.css";

const SELECT_OPTIONS = [
  "The Exorcist",
  "Hereditary",
  "The Witch",
  "Us",
  "Evil Dead",
  "It Follows",
  "The Babadook",
  "Midsommar",
  "Get Out",
];

function App() {
  return (
    <div className="App">
      <SelectOnDemand options={SELECT_OPTIONS} />
      <div
        style={{ margin: "2rem 0", border: "1px solid black", width: "100%" }}
      />
      <SelectLiveUpdate options={SELECT_OPTIONS} />
    </div>
  );
}

export default App;

Notice the two not-yet-created components: SelectOnDemand and SelectLiveUpdate. They are our two primary examples. One of React’s most powerful features is the ability to easily update the DOM based on the user’s interaction with the page elements.

However, because this feature is so fun to use, many new React developers tend to use this feature with reckless abandon. These two components will demonstrate when to use this feature with dropdown elements.

Let’s tackle the obvious one first.

A dropdown component with useState

We will now build a component that updates its state whenever the user makes a selection from the dropdown element. Often we need a component like this when we need to update other UI elements on the page when something is selected. The following example is a little contrived since the select element already displays the user’s selection natively – with or without React – but it should illustrate our point nonetheless.

To update the component's state based on the user’s selection, we will use an event handler for the select element’s onChange event and useState. Also, note how the options we passed in are used in the HTML.

import { useState } from "react";

const SelectLiveUpdate = ({ options }) => {
  const [choice, setChoice] = useState(" ");

  const handleSelect = (e) => {
    const selected = e.target.value;

    if (!selected || typeof selected !== "string") return;

    setChoice(selected);
  };

  if (!options.length) return null;

  return (
    <div>
      <h2>Select with a live update</h2>
      <p>Selected: {choice}</p>
      <select id="live-select" name="live" onChange={handleSelect}>
        <option value=" "> - - Please choose an option - - </option>
        {options.map((option) => (
          <option key={option} value={option}>
            {option}
          </option>
        ))}
      </select>
    </div>
  );
};

export default SelectLiveUpdate;

So whenever the select element changes from user interaction, the state variable choice is updated and its new value is rendered in the paragraph tag.

Here’s the rub: the component is re-rendered. If this component had any children, they might also be rerendered as a result. Depending on the component and the context of the codebase, this might be costly, and those costs can add up over time. This is the downside to using the component state.

Simply put, we don’t always need this “live update” solution. Sometimes, when a user selects an option, we only need to retrieve that data after another separate event.

A dropdown component with useRef

Now that we’ve seen the “usual” method, the one that most new React developers might reach for, let’s check out an alternative approach.

What follows is a component that does have its own state but more specifically does not re-render each time the user makes a selection. Instead, the data from the select is retrieved when the user makes another action.

In the case of the following example, the “action” is the click of a button.

import { useRef } from "react";

const SelectOnDemand = ({ options }) => {
  const selectedRef = useRef();
  const handleSubmit = () => {
    const selectEl = selectedRef.current;
    if (!selectEl) return;

    const selected = selectEl.value;
    alert(`Selected: ${selected}`);
  };

  if (!options.length) return null;

  return (
    <div>
      <h2>Select whose value is on-demand only</h2>
      <select
        id="on-demand-select"
        name="on-demand"
        ref={(el) => (selectedRef.current = el)}
      >
        <option value=" "> - - Please choose an option - - </option>
        {options.map((option) => (
          <option key={option} value={option}>
            {option}
          </option>
        ))}
      </select>

      <button onClick={handleSubmit} style={{ marginLeft: "1rem" }}>
        "Submit" (will trigger alert)
      </button>
    </div>
  );
};

export default SelectOnDemand;

Whenever the above component is rendered, an imperative variable, a reference, is made to the select HTML node itself. If you have ever used jQuery or vanilla JavaScript to build an HTML form, this reference value will be familiar to you. In our case, this value is simply an HTMLInputElement.

Because this reference is stored after the first render, we can access it when the user clicks the button and triggers the event handler. We retrieve the input element via the reference’s current property and after some basic validation, we extract the select element’s value and call the alert function.

With this method, the value of the select box is on demand, and without any unnecessary re-renders! Of course, this does not work well for us if we need to update other UI elements based on the selection as was the case in our previous example.

Summary

Ultimately, dropdowns in React only need two things:

Option data to display for the user and a means to act on the choice the user makes We covered the latter with our two examples above which comes down to whether we need only on-demand data from the dropdown or we need live updates to other UI elements based on the user’s selection from the dropdown.

However, it is worth noting that though we used static data for a dropdown’s options, it’s possible this design element be dynamic as well.

Finally, note that the above two points remain true whether we use a more humble approach with a basic select tag, or if we use something a bit more fancy or customizable by working with an input tag and an unordered list tag.