Controlled vs uncontrolled components in React Js

Last updated : 22 July 2022

The controlled component's value is bound to the React state. Any changes to the component value are written to the React state using event handlers like onChange. Therefore, the component value is available through the React state. On the other hand, the uncontrolled component is independent of the React state. The DOM handles the value and is accessible through the DOM.

Let's look at a few HTML elements and how we can use them as controlled components.

React controlled input element example

The value={name} ensures the input value always represents the corresponding state variable value. The onChange={setName(e.target.value)}} updates the state variable with changes to the input element value.

import { useState } from "react"
export const App = () => {
const [name, setName] = useState<string>()
const handleReset = () => {
  setName("")
}
return(
  <div>
    <input value={name} onChange={(e) => setName(e.target.value)}/>
  </div>
  <div>{name}</div>
  <br/>
  <div>
    <button onClick={handleReset}>Reset</button>
 </div>
)
}
export default App
React controlled input element example
Figure 1: React controlled input element example

React controlled select element example

The value={name} binds the select value to the state variable name. The onChange={(e) => setName(e.target.value)} updates the state variable when the select option changes. Using a controlled select element makes it easy to set the selected option. Unlike in HTML, you don't have to check for the selected value in the option. Just set the select value state variable to the desired option value and React Js will take care of the rest.

import { useState } from "react"
export const App = () => {
const [countryName, setCountryName] = useState<string>()
const selectOptions = [{val:"0", label:"Select"},{val:"1", label:"USA"},{val:"2", label:"Canada"}]
return(
  <div>
    <select value={countryName} onChange={(e) => setCountryName(e.target.value)}>
        {selectOptions.map((option) => {
          return <option value={option.val} key={option.val}>{option.label}</option>
        })}
    </select>
  </div>
  <div>
    <button onClick={() => setCountryName("2")}>Select Canada</button>
  </div>
  <div>
    {countryName && selectOptions.filter((country) => {
      return country.val === countryName
    })[0].label}
  </div>
 )
}
export default App
React controlled select element example
Figure 2: React controlled select element example

React controlled multiple select examples

Like select element, the value={selected} binds the selected options to the state variable. The onChange={(e) => handleChange(e.target)} binds the selected values to the state variable. The difference is the selected state variable is an array.

import { useState } from "react"
export const App = () => {
const [selected, setSelected] = useState<string[]>()
const selectOptions = [
                        {val:"1", label:"USA"},
                        {val:"2", label:"Canada"},
                        {val:"3", label:"Japan"},
                        {val:"4", label:"Mexico"}
                      ]
const handleChange = (target: HTMLSelectElement) => {
  setSelected(Array.from(target.selectedOptions, option => option.value))
  selectOptions.filter((option) => {return option.label})
}
const selectCanada = () => {
  setSelected(["2"])
}
return(
  <>
  <select name="multiSelect" multiple={true} value={selected} onChange={(e) => handleChange(e.target)}>
    {selectOptions.map((option) => {
        return <option value={option.val} key={option.val}>{option.label}</option>
    })}
  </select>
  <br/>
  <button onClick={selectCanada}>Select Canada</button><br/>
  <ol type="1">
    {selected?.map((val) => {
     return <li>{selectOptions.filter((option) => {return option.val === val})[0].label}</li>
    })}
  </ol>
  </>
 )
}
export default App
React controlled multiple select examples
Figure 3: React controlled multiple select examples

React controlled radio button example

Note that in Radio button groups, we don't directly bind the radio button value to the state variable. To check a particular radio button manually, we check if the state variable value is equal to each radio button value.

import { useState } from "react"
export const App = () => {
const [radioButtonName, setRadioButtonName] = useState<string>()
const selectUSA = () => {
  setRadioButtonName("USA")
}
return(
  <div style={{margin: 24}}>
    <div>
      <label style={{width: 76}} htmlFor="">USA</label>
      <input value="USA" checked={radioButtonName === "USA"} type="radio" onChange={(e) => setRadioButtonName(e.target.value)}/>
    </div>
    <div>
      <label style={{width: 76}} htmlFor="">Canada</label>
      <input value="Canada" checked={radioButtonName === "Canada"} type="radio" onChange={(e) => setRadioButtonName(e.target.value)}/>
    </div>
    <div>
      <label style={{width: 76}} htmlFor="">Japan</label>
      <input value="Japan" checked={radioButtonName === "Japan"} type="radio" onChange={(e) => setRadioButtonName(e.target.value)}/>
    </div>
    <div>
      <label style={{width: 76}} htmlFor="">Mexico</label>
      <input value="Mexico" checked={radioButtonName === "Mexico"} type="radio" onChange={(e) => setRadioButtonName(e.target.value)}/>
    </div>
    {radioButtonName && <div style={{marginTop: 24}}>{radioButtonName} Selected</div>}
    <br/>
    <button onClick={selectUSA}>Select USA</button><br/>
  </div>
 )
}
export default App
React controlled radio button example
Figure 4: React controlled radio button example

React controlled checkbox example

Like the radio button example above, we don't directly bind the check box value to the state variable. Instead, we use a handler method to update the state variable when the checkbox is checked and unchecked.

import { useState } from "react"
export const App = () => {
const [checkBoxValue, setCheckBoxValue] = useState<string>()
const subscribeToNewsLetter = (val: HTMLInputElement) => {
  setCheckBoxValue((prev) => val.checked ? val.value : "N")
}
return(
  <div style={{margin: 24}}>
    <div>
      <label style={{width: 224}}>Subscribe to news letter : </label>
      <input type="checkbox" value="Y" onChange={(e) => subscribeToNewsLetter(e.target)}/>
    </div>
  {checkBoxValue && <div style={{marginTop: 24}}>{checkBoxValue} Selected</div>}
  </div>
 )
}
export default App
React controlled checkbox example
Figure 5: React controlled checkbox example

React controlled checkbox group example

We use a string array to hold the checked checkboxes values. Check and uncheck are determined by the event, and add or delete the checkbox value from the state array accordingly.

import { useState } from "react"
export const App = () => {
const [checkBoxValues, setCheckBoxvalues] = useState<string[]>([])
const subscribeToNewsLetter = (target: HTMLInputElement) => {
  if(target.checked){
    setCheckBoxvalues((prev) => {
      return [...prev, target.value]
    })
  }
  else{
    setCheckBoxvalues((prev) => {
      return prev.filter((country) => {
        return country !== target.value
      })
    })
  }
}
return(
  <div style={{margin: 24}}>
    <div>
      <label style={{width: 76}} htmlFor="">USA</label>
      <input type="checkbox" value="USA" onChange={(e) => subscribeToNewsLetter(e.target)}/>
    </div>
    <div>
      <label style={{width: 76}} htmlFor="">Canada</label>
      <input type="checkbox" value="Canada" onChange={(e) => subscribeToNewsLetter(e.target)}/>
    </div>
    <div>
      <label style={{width: 76}} htmlFor="">Japan</label>
      <input type="checkbox" value="Japan" onChange={(e) => subscribeToNewsLetter(e.target)}/>
    </div>
    <div>
      <label style={{width: 76}} htmlFor="">Mexico</label>
      <input type="checkbox" value="Mexico" onChange={(e) => subscribeToNewsLetter(e.target)}/>
    </div>
    <div style={{marginTop: 24}}>Selected options</div>
    <ol type="1">
      {checkBoxValues?.map((val) => {
        return <li>{val}</li>
      })}
    </ol>
  </div>
 )
}
export default App
React controlled checkboxes group
Figure 6: React controlled checkboxes group
Lance
By: Lance
Lance is a software engineer with over 15 years of experience in full-stack software development.
Read more...

Leave a comment

No Comments