Translation | React form: Refs usage

Posted by whare on Sat, 15 Jun 2019 00:28:42 +0200

Translation | React form: Refs usage

React provides two standard methods for getting values from <form> elements. The first approach is to implement so-called controlled components (see me). Articles published in blogs The second method is to use the ref attribute of React.

Controlled components are heavy, and the displayed values and component state bindings are its characteristics. We update the displayed values by executing an onChange event handle attached to the form element. The onChange function updates the state attribute, which in turn updates the value of the form element.

(Before reading the following article, if you just want to see the corresponding sample code: Please step here.)

Examples of controlled components:

import React, { Component } from 'react';

class ControlledCompExample extends Component {
  constructor() {
    super();
    this.state = {
      fullName: ''
    }
  }
  handleFullNameChange = (e) => {
    this.setState({
      fullName: e.target.value
    })
  }
  handleSubmit = (e) => {
    e.preventDefault();
    console.log(this.state.fullName)
  }
  render() {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <label htmlFor="fullName">Full Name</label>
          <input
            type="text"
            value={this.state.fullName}
            onChange={this.handleFullNameChange}
            name="fullName" />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

export default ControlledCompExample;

The input value is this.state.fullName (lines 7 and 26). The onChange function is handleFullNameChange (lines 10 - 14 and 27).

The main advantages of controlled components are:
1. Easy to verify user input
2. Other components can be rendered dynamically according to the values of controlled components. For example, the value a user selects in the drop-down list (such as "dog" or "cat") can control other form components rendered in the form (such as a check box for setting varieties)

The disadvantage of controlled components is that they write a lot of code. You need props to pass the state attribute to the form element, and a function to update the value of the attribute.

This is really not a problem for a single form element -- but if you need a large and complex form (without dynamic rendering or real-time validation), overuse of controlled forms can make you write tons of code.

A simple way to get values from form elements is to use ref attributes. We deal with different form elements and component structures in different ways, so the rest of this article is divided into the following sections.

  1. Text Input Box, Digital Input Box and Selection Box
  2. The child component passes the value to the parent component through props
  3. Radio tag set
  4. Checkbox tag set

1. Text Input Box, Digital Input Box and Selection Box

The simplest examples of ref are text and digital input elements. In the ref attribute of input, we add an arrow function that takes input itself as a parameter. I like to name parameters the same as the element itself, as in the third line below:

<input
  type="text"
  ref={input => this.fullName = input} />

Since this parameter is an alias for the input element itself, you can name it as you like:

<input
  type="number"
  ref={cashMoney => this.amount = cashMoney} />

Then you can take this parameter and assign it to the property mounted on the this keyword in the current class. input (for example, DOM nodes) can be read through this.fullName and this.amount. Its value can be read by this.fullName.value and this.amount.value.

Selecting elements can also be done in the same way (for example, drop-down lists).

<select
  ref={select => this.petType = select}
  name="petType">
  <option value="cat">Cat</option>
  <option value="dog">Dog</option>
  <option value="ferret">Ferret</option>
</select>

The value of the selected element can be obtained through this.petType.value.

2. The child component passes the value to the parent component through props

With controlled components, it's very easy for parent components to get the value of child components -- this value already exists in parent components! It is passed to the subcomponent. At the same time, the onChange method is passed to the subcomponent, and the user updates the value by interacting with the UI.

You can stay with me. Last article You can see how it works in the controlled component example.

Although this value already exists in the parent component of the controlled component, it is not the case when ref is used. When ref is used, the value exists in the DOM node itself and must communicate with the parent component upwards.

To pass this value from the child component to the parent component, the parent component needs to pass a hook to the child component. The child component then mounts the node onto the hook for the parent component to read.

Before we go deeper, let's look at some code.

import React, { Component } from 'react';

class RefsForm extends Component {
  handleSubmit = (e) => {
    e.preventDefault();
    console.log('first name:', this.firstName.value);
    this.firstName.value = 'Got ya!';
  }
  render() {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <CustomInput
            label={'Name'}
            firstName={input => this.firstName = input} />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

function CustomInput(props) {
  return (
    <div>
      <label>{props.label}:</label>
      <input type="text" ref={props.firstName}/>
    </div>
  );
}

export default RefsForm;

From the above code, you can see a form component RefForm and an input component called CustomInput. Normally, the arrow function is on the input itself, but you can see from this (lines 15 - 27) that it is passed through props. Because the arrow function exists in the parent component, this in this.firstName points to the parent component.

The value of the input child component is assigned to the parent component's this.firstName property, so the parent component can get the value of the child component. Now, this.firstName in the parent component refers to the DOM node in the child component (for example, input in CustomInput).

The parent component can not only access DOM nodes in input, but also assign values to nodes in the parent component. You can see the example in line 7 above. Once the form is submitted, the input value is set to "Got ya!".

It's a bit confusing, so please read and practice the code carefully until you fully understand it.

You may write better controlled components for radio s and checkbox es, but if you really want to use `ref', the next two parts will help you.

3. Radio Label Collection

Unlike input elements such as text and number, radio elements appear in groups. Elements in each group have the same name attribute, like this:

<form>
  <label>
    Cat
    <input type="radio" value="cat" name="pet" />
  </label>
  <label>
    Dog
    <input type="radio" value="dog" name="pet" />
  </label>
  <label>
    Ferret
    <input type="radio" value="ferret" name="pet" />
  </label>
  <input type="submit" value="Submit" />
</form>

There are three options in the "pet" radio tag set - "cat", "dog" and "ferret".

Since we are concerned with the elements of the entire collection, it is not a good idea to set ref for each radio box. Unfortunately, no DOM node contains the radio set.

The value of the radio set can be retrieved in the following three steps:
1. Set ref on the form tag (line 20 below).
2. Remove the radio set from the form. Then it should be the pet collection (line 9 below).

  • A list of nodes and a value are returned here. In this case, the list of nodes contains three input nodes and the selected values.
  • Note that this list of nodes is an array of classes, which has no method of arrays. More on this topic in the next section.
    3. Use. method to get the value of this collection (line 13 below).
import React, { Component } from 'react';

class RefsForm extends Component {

  handleSubmit = (e) => {
    e.preventDefault();

    //  Remove the list of nodes from form
    //  It's an array of classes, no method of arrays
    const { pet } = this.form;

    // The set of radio tags has value attributes
    // View Printed Data
    console.log(pet, pet.value);
  }

  render() {
    return (
      <div>
        <form
          onSubmit={this.handleSubmit}
          ref={form => this.form = form}>
          <label>
            Cat
            <input type="radio" value="cat" name="pet" />
          </label>
          <label>
            Dog
            <input type="radio" value="dog" name="pet" />
          </label>
          <label>
            Ferret
            <input type="radio" value="ferret" name="pet" />
          </label>
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

export default RefsForm;

It's also possible if you're writing a form with subcomponents. Although there will be more logic in the component, the way to get values from the radio set is invariable.

import React, { Component } from 'react';

class RefsForm extends Component {
  handleSubmit = (e) => {
    e.preventDefault();

    //  Remove the list of nodes from form
    //  It's an array of classes, no method of arrays
    const { pet } = this.form;

    // The set of radio tags has value attributes
    // View Printed Data
    console.log(pet, pet.value);
  }

  render() {
    return (
      <div>
        <form
          onSubmit={this.handleSubmit}
          ref={form => this.form = form}>
          <RadioSet
            setName={'pet'}
            setOptions={['cat', 'dog', 'ferret']} />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

function RadioSet(props) {
  return (
    <div>
      {props.setOptions.map(option => {
        return (
          <label
            key={option}
            style={{textTransform: 'capitalize'}}>
            {option}
            <input
              type="radio"
              value={option}
              name={props.setName} />
          </label>
        )
      })}
    </div>
  );
}

export default RefsForm;

4. Checkbox tag set

Unlike the radio tag set, the Checkbox tag set may have multiple values. As a result, it's harder to get these values than it is to get the values of the set of radiolabels.

The selected values of the checkbox tag set can be retrieved in the following five steps:

1. Set ref on the form tag (line 27 below).
2. Remove the checkbox collection from the form. Then it should be the pet collection (line 9).

  • Here returns a list of nodes and a value
  • Note that this list of nodes is an array of classes, it has no method of arrays, and then we need to do the following step.
    3. Convert this list of nodes into an array, and then you can use the array method (checkbox Array in line 12).
    4. Use Array.filter() to get the checkbox selected (checked Checkboxes in line 15).
    5. Use Array.map() to get the unique value of the checkbox selected (checked Checkboxes Values in line 19)
import React, { Component } from 'react';

class RefsForm extends Component {
  handleSubmit = (e) => {
    e.preventDefault();

    //  Remove the list of nodes from form
    //  It's an array of classes, no method of arrays
    const { pet } = this.form;

    // Convert the list of nodes into an array
    const checkboxArray = Array.prototype.slice.call(pet);

    // Remove only the checkbox selected
    const checkedCheckboxes = checkboxArray.filter(input => input.checked);
    console.log('checked array:', checkedCheckboxes);

    // Use the. map() method to extract values from each checkbox selected
    const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value);
    console.log('checked array values:', checkedCheckboxesValues);
  }

  render() {
    return (
      <div>
        <form
          onSubmit={this.handleSubmit}
          ref={form => this.form = form}>
          <label>
            Cat
            <input type="checkbox" value="cat" name="pet" />
          </label>
          <label>
            Dog
            <input type="checkbox" value="dog" name="pet" />
          </label>
          <label>
            Ferret
            <input type="checkbox" value="ferret" name="pet" />
          </label>
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

export default RefsForm;

Writing checkbox with subcomponents is the same as writing radio s in the previous section.

import React, { Component } from 'react';

class RefsForm extends Component {
  handleSubmit = (e) => {
    e.preventDefault();

    //  Remove the list of nodes from form
    //  It's an array of classes, no method of arrays
    const { pet } = this.form;

    // Convert the list of nodes into an array
    const checkboxArray = Array.prototype.slice.call(pet);

    // Remove only the checkbox selected
    const checkedCheckboxes = checkboxArray.filter(input => input.checked);
    console.log('checked array:', checkedCheckboxes);

    // Use the. map() method to extract values from each checkbox selected
    const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value);
    console.log('checked array values:', checkedCheckboxesValues);
  }

  render() {
    return (
      <div>
        <form
          onSubmit={this.handleSubmit}
          ref={form => this.form = form}>
          <CheckboxSet
            setName={'pet'}
            setOptions={['cat', 'dog', 'ferret']} />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

function CheckboxSet(props) {
  return (
    <div>
      {props.setOptions.map(option => {
        return (
          <label
            key={option}
            style={{textTransform: 'capitalize'}}>
            {option}
            <input
              type="checkbox"
              value={option}
              name={props.setName} />
          </label>
        )
      })}
    </div>
  );
}

export default RefsForm;

conclusion

If you don't need:

1. Monitor the values of form elements in real time (for example, components rendered after user input)
2. Real-time execution of custom validation method

So using ref method to get the value of form element is a good method.

In most cases, the most important value of ref over controlled components is to write less code. Checkbox (radio second) is a special case. For checkbox, the amount of code saved by ref is very small, so it is impossible to say whether it is better to use controlled components or Ref.

Topics: React Attribute REST less