Using state with form controls in React

Author
By Darío Rivera
Posted On in React

After learning the fundamentals of React state, it’s time to apply them in real-world scenarios. Practicing with useState to handle UI interactions is a great next step. In this article, we’ll see how to synchronize state with various HTML form controls.

Input text

Take a look at the following example. The state variable name is defaulted to Steave. The onChange prop propagates the input's value to the setName setter, which updates the state.

import { useState } from "react";

function App() {
  const [name, setName] = useState("Steve");

  return (
    <div>
      <input
        type="text"
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <p>Hello {name}</p>
    </div>
  );
}

e.target.value is the way you access the actual data. The e represents the synthetic event object that React creates. Once you access .target it becomes a native DOM element (HTMLElement in most of the cases), so you can use .value to get the actual value.


Radio button

Radio buttons map to a single value. You already know how the prop onChange works with e.target.value triggering a new render when the user changes the selection. To be able to show the user selection, we have used gender === "male" and gender === "female", so the option is mutually exclusive.

function App() {
  const [gender, setGender] = useState("male");

  return (
    <div>
      <label>
        <input
          type="radio"
          value="male"
          checked={gender === "male"}
          onChange={e => setGender(e.target.value)}
        />
        Male
      </label>

      <label>
        <input
          type="radio"
          value="female"
          checked={gender === "female"}
          onChange={e => setGender(e.target.value)}
        />
        Female
      </label>

      <p>Selected: {gender}</p>
    </div>
  );
}

Here's how the above example looks in CodePen.


Textarea

Textarea works similar to an input text.

function App() {
  const [content, setContent] = useState("Hello\nWorld");

  return (
    <div>
      <textarea
        value={content}
        onChange={e => setContent(e.target.value)}
      />
      <pre>{content}</pre>
    </div>
  );
}

Notice the value of content is shown inside a <pre> tag so it can render the new lines.


Select

For select inputs the onChange prop must be used with a select tag.

function App() {
  const [country, setCountry] = useState("CA");

  return (
    <>
      <select value={country} onChange={e => setCountry(e.target.value)}>
        <option value="CA">Canada</option>
        <option value="US">United States</option>
        <option value="CO">Colombia</option>
      </select>

      <p>Selection: {country}</p>
    </>
  );
}

Let's check how this works in CodePen.


Single Checkbox

For a single checkbox, state handling is similar to a text input. One caveat is that in plain HTML, the default value of a checkbox is "on". In React, you should use the checked prop to control whether the checkbox is selected or not.

import React, { useState } from "https://esm.sh/react@19";
import { createRoot } from "https://esm.sh/react-dom@19/client";

function App() {
  const [notificationsEnabled, setNotificationsEnabled] = useState(true)
  
  return (
    <>
      <label>
        <input
          type="checkbox"
          checked={notificationsEnabled}
          onChange={(e) => {
            setNotificationsEnabled(e.target.checked)
          }}
        />
        I want to receive notifications
      </label><br/><br/>
      Notifications are: {notificationsEnabled ? "ENABLED" : "DISABLED"}
    </>
  )  
}

const root = createRoot(document.getElementById("app"))
root.render(<App />)

Let's check how this works in CodePen.


Multiple Checkboxes

There are several ways to track the state for multiple interrelated checkboxes. In the following example, I've used a simple array to store the different values of these tags in the state. Take a look at how the onChange prop uses e.targe.value to conditionally trigger changes in the state using either addFeed or removeFeed.

import React, { useState } from "https://esm.sh/react@19";
import { createRoot } from "https://esm.sh/react-dom@19/client";

function App() {
  const [feeds, setFeeds] = useState(['traveling']);

  function addFeed(feed) {
    setFeeds([...feeds, feed])
  }
  
  function removeFeed(feed) {
    setFeeds(feeds.filter(item => item !== feed))
  }

  return (
    <>
      <label>
        <input
          type="checkbox"
          checked={feeds.includes("technology")}
          onChange={(e) => {
            e.target.checked ? addFeed("technology") : removeFeed("technology")
          }}
          />
        Technology
      </label>
      <br/>
      <label>
        <input
          type="checkbox"
          checked={feeds.includes("traveling")}
          onChange={(e) => {
            e.target.checked ? addFeed("traveling") : removeFeed("traveling")
          }}
          />
        Traveling
      </label>
      <p>Interests: {feeds.join(', ')}</p>
    </>
  );
}

const root = createRoot(document.getElementById("app"))
root.render(<App />)

Finally, let's check how this looks in CodePen.



Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.