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

Hello, Habr! I present to you the translation of the article "Controlled and uncontrolled form inputs in React don't have to be complicated" by Gosha Arinich.

You may have seen many articles saying, “you should not use“ setState ”, while the documents claim that“ refs are bad. ”This is all very contradictory. Sometimes, it’s hard to figure out how to get things right, and what are the criteria for choosing between these methods.

So how do you make forms? After all, forms are central to many web applications. And yet, form processing in React is the cornerstone, isn't it?

However, it's not so hard Let me show you the differences between these approaches, as well as when you should use each of them.

Unmanaged components


Unmanaged components are like regular HTML forms:

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

They remember everything that you typed. Then you can get their value using ref. For example, in the onClick handler:


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>
    );
  }
}

In other words, you need to “pull” the values ​​out of the field when you need it. This can be done when submitting the form.

This is the easiest way to implement forms. Of course, there must be good reason for using it, namely: the simplest forms, either during the study of React.
However, this method is not so flexible, so let's take a better look at managed components.

Managed Components:


The managed component accepts its current value as props, as well as a callback to change this value. You can say that this is a more “reactive” way to control the component, however this does not mean that you should always use this method.

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

This is all very good, but the value of the input form must exist in some state. Typically, a component that renders an input form (i.e. a form) saves them in its state:


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>
    );
  }
}

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

Each time you enter a new character, handleNameChange is called. It takes the new value of the input form and writes it to state.

image

  • It all starts with an empty line - '';
  • You enter the letter 'a' and handleNameChange gets it and calls setState. Then the input form is rendered again with the value 'a';
  • You enter the letter 'b' and handleNameChange gets the value 'ab' and sets it to state. The Opet input form is rendered, but now with the value 'ab'.

This stream seems to “push” changes into the form, which is why the component always has the current value of the input data, without requiring an explicit update request.

This means that your data (state) and user interface (input form) are always in sync. State gives value to the form, while form changes the current state value.

It also means that forms can respond immediately to changes, which in turn is necessary to:

  • quick feedback, such as validation;
  • disable a specific button until all form fields are valid;
  • enabling the processing of certain input field formats, such as credit card numbers.

But if you do not need it and you think that unmanaged components are much simpler, then use them.

What makes a component “manageable”?


Of course, there are other form elements, such as: checkboxes, radio, textarea and select.
A component becomes manageable when you set its value use props. That's all.

However, each of the form elements has different ways of setting the value, so here is a small table for general understanding:

ElementValueCallback for changeNew value in callback
<input type="text" />
value = "string"onChangeevent.target.value
<input type="checkbox" />
checked = {boolean}onChangeevent.target.checked
<input type="radio" />
checked = {boolean}onChangeevent.target.checked
<textarea />
value = "string"onChangeevent.target.value
<select />
value = "option value"onChangeevent.target.value

findings


Both managed and unmanaged components have advantages and disadvantages. Evaluate a specific situation and choose an approach - if this works for you, then why not take advantage of it?

If your form is incredibly simple in terms of interacting with the user interface, then unmanaged components are perfect for you. You do not have to listen to what various articles say is bad.

image

In addition, choosing a component type is not a decision that is made once and for all: you can always replace unmanaged components with managed ones. The transition from one to another is not so difficult.

All Articles