React Forms - Data Binding - Controlled and Uncontrolled inputs

Published on
6 mins read
––– views

Forms

Forms are crucial for commercial websites as they allow companies to receive feedback from their customers. They offer an opportunity for customers to voice their questions or concerns and allow the company to find solutions. That's what being in business is all about: solving problems.

To stay in touch with users, forms are a must-have, and we can create them using React, which is essential. There are numerous packages that promise to simplify React Form implementation, but are they really required?

Data Binding

In web application development, it's common to associate a form input with a specific state. For instance, the value of a "username" input should be linked to a username state. This concept is referred to as "data binding". Most front-end frameworks provide a method to bind a specific state to a specific form element.

With data binding, an app can even check your location and determine whether it's night or day. Based on this information, the app can greet you with Good Morning or Good Evening. So, data binding not only helps with tracking information, but it can also bring a more personalized experience to the user.

Let's create a search box, did you know that Google's main page is just a form?

function SearchForm() {
  const [search, setSearch] = React.useState('guitars')

  return (
    <>
      <form>
        <label htmlFor="search-box">Search:</label>
        <input
          type="text"
          id="search-box"
          value={search}
          onChange={(event) => {
            setSearch(event.target.value)
          }}
        />
      </form>
      <p>Search term: {search}</p>
    </>
  )
}

export default SearchForm

Let's break this code down a bit to understand it better.

<input
  type="text"
  id="search-box"
  value={search}
  onChange={(event) => {
    setSearch(event.target.value)
  }}
/>

In React, to make an input field editable, we need to bind its value to a state variable using the useState hook. Our code sets the initial value of the state variable to "guitars" and uses it to set the value of the input field.

When the user types in the input field, the onChange event is triggered, which calls the setSearch function to update the state variable with the new value entered by the user. This causes the input field to be updated with the user's input and also updates the state variable with the new value.

what does onChange={(event) => {setSearch(event.target.value) mean?

event-target-value refers to the value inside the input field. target is basically where the user will type the text (input box), and value is the text typed by the user. We use setSearch() function to update the input text.

The word event can be anything, but most developers will choose the letter e or event to make their code more readable.


const [search, setSearch] = React.useState('guitars')

<input value={search} />

if we change the default value "guitars", the value will change too because it is linked to {search} and {search} is assigned to a default value.

Controlled inputs

In React, a controlled input is when you let React take control of an input field, instead of the input managing its own state. This way, you can keep track of the input's value, validate it, and update the form's behavior based on what the user types.

To create a controlled input we need a state variable (useState hook), Then we need to set input's value attribute to the state variable. After that, Input's onChange event handler will update the state with useState hook. Other hooks like useReducer and useRef can be used to create controlled inputs too, but useState is the most common way of doing.

This is a controlled input. Because we set the state with useState, input's variable is set to {inputValue} and onChange will change the state. React is managing the state.

function MyForm() {
  const [inputValue, setInputValue] = useState('')

  return (
    <form>
      <label>
        Name:
        <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
      </label>
      <button type="submit">Submit</button>
    </form>
  )
}

Uncontrolled inputs

To keep things short, if we don't set the value it is an Uncontrolled element. React does not manage the state of the input. The input's value is determined by the user. Uncontrolled input won't have validation or some kind of advanced function. The user will be able to type anything inside.

Uncontrolled input cannot validate if the address the user has entered is valid or not. So, we need to be careful when to use uncontrolled inputs. To provide great user experience, we need to warn our users if the data they have entered isn't correct.

Controlled or Uncontrolled?

React is fine with either and won't scream at us for choosing one over the other. The thing to remember is that, React doesn't like it in between. The input must be 100% controlled, or 100% uncontrolled. If we make it one way and flip it to the other, it can cause serious bugs.

This code will throw an error, do you know why?

const [username, setSearch] = useState()

// some code for form, label etc. not related
<input
        type="text"
        id="search"
        value={search}
        onChange={event => {
          setSearch(event.target.value);
        }}
      />

We set the useState hook to basically nothing, so it is undefined. undefined cannot be controlled, because React doesn't know what it is. So, the first state of our form is uncontrolled.

Then -->

<input
        type="text"
        id="search"
        value={search}
        onChange={event => {
          setSearch(event.target.value);
        }}
      />
      ```

We entered {search} for the value attribute. That is how you create a controlled input. So, state of our form starts as uncontrolled but once the user types in anything, the setSearch will let the React control the input. React doesn't like that.

const [search, setSearch] = useState('')

This is how you would solve this problem. useState("") is a defined value. It's an empty string, but it isn't undefined. First state of the input is controlled because an empty string a defined variable.

// this is controlled, because of useState('')
const [search, setSearch] = useState('')
// this is uncontrolled, because useState() equals to undefined and we cannot control undefined.
const [search, setSearch] = useState()