Controlled and uncontrolled form inputs in React don't have to be complicated

07 Nov 2016

You may have seen many articles saying “you shouldn’t use setState,” and the docs are claiming refs are bad”… That is so contradictory. It’s hard to understand how to “get it right” and even what are the criteria for choosing.

How the hell are you supposed to make forms?

After all, forms are central to many web apps out there. And yet, form handling in React seems to be a bit of a… corner stone?

Fear no more. Let me show you the differences between the approaches, as well as when you should use each.

Uncontrolled

Uncontrolled inputs are similar to traditional HTML form inputs:

class Form extends Component {
  render() {
    return (
      <div>
        <input type="text" />
      </div>
    );
  }
}

It remembers what you typed. You can then get its value using a ref. For example, in onClick handler of a button:

class Form extends Component {
  handleSubmitClick = () => {
    const name = this._name.value;
    // do something with `name`
  }

  render() {
    return (
      <div>
        <input type="text" ref={input => this._name = input} />
        <button onClick={this.handleSubmitClick}>Sign up</button>
      </div>
    );
  }
}

Put differently, you have to ‘pull’ the value from the field when you need it. This typically happens when the form is submitted.

That is the simplest way to implement the form inputs. There certainly are valid cases for using it: in simple forms in the real world; and when learning React.

It’s not as powerful, though, so let’s see those controlled inputs next.

Controlled

A controlled input accepts its current value as a prop, as well as a callback to change that value. You could say it’s a more “React way” of approaching this (which doesn’t mean you should always use it).

<input value={someValue} onChange={handleChange} />

Which is fine and all… but the value of this input has to live in the state somewhere. Typically, the component that renders the input (aka the form component) saves that in its state:

(Of course, it can be in the state of another component, or even in the separate state store, like Redux.)

class Form extends Component {
  constructor() {
    super();
    this.state = {
      name: '',
    };
  }

  handleNameChange = (event) => {
    this.setState({ name: event.target.value });
  };

  render() {
    return (
      <div>
        <input
          type="text"
          value={this.state.name}
          onChange={this.handleNameChange}
        />
      </div>
    );
  }
}

Every time you type a new character, handleNameChange is called. It takes in the new value of the input and sets it in the state.

This flow kind of ‘pushes’ the value changes to the form component, so the Form component always has the current value of the input, without needing to ask for it explicitly.

This means your data (state) and UI (inputs) are always in sync. The state gives the value to the input, and the input asks the Form to change the current value.

This also means that the form component can respond to input changes immediately; for example, by:

But if you don’t need any of that and consider uncontrolled to be simpler, go for it.

What makes an element “controlled”

There are other form elements, of course. You have checkboxes and radios and selects and textareas.

A form element becomes “controlled” if you set its value via a prop. That’s all.

Each of the form elements, though, has a different prop for setting that value, so here’s a little table to summarize:

Element Value property Change callback New value in the callback
<input type="text" /> value="string" onChange event.target.value
<input type="checkbox" /> checked={boolean} onChange event.target.checked
<input type="radio" /> checked={boolean} onChange event.target.checked
<textarea /> value="string" onChange event.target.value
<select /> value="option value" onChange event.target.value

Conclusion

Both the controlled and uncontrolled form fields have their merit. Evaluate your specific situation and pick the approach — what works for you is good enough.

If your form is incredibly simple in terms of UI feedback, uncontrolled with refs is entirely fine. You don’t have to listen to what the various articles are saying is “bad.”

feature uncontrolled controlled
one-time value retrieval (e.g. on submit)
instant field validation
conditionally disabling submit button
enforcing input format
several inputs for one piece of data
dynamic inputs

References

BTW. If you want to learn more about handling forms with React, be sure to sign up for my newsletter.

Get a free sample of The Missing Forms Handbook of React

You know how you tear your hair out when your designer asks to make a nice form?

But what if you could implement the form experience your users deserve?

The Missing Forms Handbook of React can help you breeze through your React forms. You will learn how forms fit into the React model. And, you are going to see how to implement common form patterns and structures.

No spam, promise. I hate it as much as you do!

, enjoying the article? Now think of 3 friends who are interested in React, Forms and would be into it, and share the link with them! 👇

https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/