preface

You wish the world, I wish you no bugs. Hello everyone! I’m Lin Dull.

Recently all did not how output 😂, not stop more notice is “soft text”, or a little embarrassed. The problem is not big, my way (skin) modest (thick) cough up 😄.

So let’s talk a little bit more about technology today, which is the title of this article — Controlled and Uncontrolled Components.

The reason for writing this article is that I was fascinated by the content of controlled and uncontrolled components involved in HOC writing, and then I found that there was a lot of content that could be said. However, I searched the online textbooks and found that most of what was said was confusing and not easy for beginners to understand.

So dull is also hoping to play their strengths will this part of the content said short and fine, convenient for everyone to understand.

(Yes, long is what you want, not short…)

Ok 👌, no more 😁, here’s what you can learn by reading this article:

  • Basic concepts of controlled components
  • Select controlled Components
  • Dynamic form controlled component example
  • Uncontrolled component
  • Special file tag

The body of the

Basic concepts of controlled components

We can guess what these two words mean by their names:

  • Controlled components: Components under our control
  • Non-controlled components: components that are not under our control

(This is not the timo nonsense…)

Ahem, ok, what do we mean by controlled and uncontrolled? This is our control over the state of a component, and whether its value can only be set by the user, not by code.

React defines an input box that doesn’t have the same two-way binding as Vue’s V-Model. That is, we don’t have a command that combines the data with the input field, where the user enters something and the data is updated synchronously.

Like this case:

class TestComponent extends React.Component {
  render () {
    return <input name="username" />}}Copy the code

It maintains a “state” of its own when the user enters something into the interface’s input box, so that it can update the UI itself based on the user’s input. (This state is not the usual this.state, but the abstract state on each form element.)

What if we wanted to control the content of the input field? Well… The content of the input field depends on the value attribute in the input field, so we can define a property named username in this.state and specify the value on the input as this attribute:

class TestComponent extends React.Component {
  constructor (props) {
    super(props);
    this.state = { username: 'lindaidai' };
  }
  render () {
    return <input name="username" value={this.state.username} />}}Copy the code

The value is controlled by this.state.username. When the user enters new content, this.state.username is not automatically updated, so the content in the input will not change.

As you might already know, we can use an onChange event to listen for input changes and update this.state.username with setState:

class TestComponent extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      username: "lindaidai"
    }
  }
  onChange (e) {
    console.log(e.target.value);
    this.setState({
      username: e.target.value
    })
  }
  render () {
    return <input name="username" value={this.state.username} onChange={(e)= > this.onChange(e)} />}}Copy the code

The state and UI are now updated regardless of what the user enters, and we can use this.state.username elsewhere in the component to retrieve the contents of the input, and we can also modify the contents of the input by using this.setstate ().

OK👌, now let’s look at the definition of a controlled component:

In HTML form elements, they usually maintain their own set of states and make their own UI updates as the user enters. This behavior is not controlled by our program. If the state attribute in React is dependent on the value of the form element, and the onChange event is combined with setState() to update the state attribute, the operations on the form can be controlled during user input. The form input elements that React controls their values in this way are called controlled components.

(Well, I think the above 👆 summary can be used in the interview)

Select controlled Components

I’ve shown you a basic controlled component using input above, but it works pretty much the same for other form elements, probably with different attribute names and events.

For example, form elements whose input type is text use:

  • value
  • onChange

The textarea tag is also used with value and onChange:

<textarea value={this.state.value} onChange={this.handleChange} />
Copy the code

Radio select

For select form elements, React’s conversion to a controlled component may differ somewhat from native HTML.

In native, we use selected by default for a select option, such as the following:

<select>
  <option value="sunshine">The sun</option>
  <option value="handsome">handsome</option>
  <option selected value="cute">lovely</option>
  <option value="reserved">High cold</option>
</select>
Copy the code

Set the “cute” option to selected and it will be selected by default.

However, writing with a React controlled component is less troublesome because it allows the value attribute to be used on the root select tag to control which one is selected. This way, it’s also easier for us to just update it in the root tag after each user reselect, as in this case 🌰 :

class SelectComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: 'cute' };
  }
  handleChange(event) {
    this.setState({value: event.target.value});
  }
  handleSubmit(event) {
    alert('What kind of date are you dating today?' + this.state.value);
    event.preventDefault();
  }
  render() {
    return (
      <form onSubmit={(e)= > this.handleSubmit(e)}>
        <label>What type of blind date are you dating today?<select value={this.state.value} onChange={(e)= > this.handleChange(e)}>
            <option value="sunshine">The sun</option>
            <option value="handsome">handsome</option>
            <option value="cute">lovely</option>
            <option value="reserved">High cold</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>); }}export default SelectComponent;
Copy the code

You can see that controls with input type text and Textarea and SELECT are implemented equally as controlled components.

Multiple select

For multiple select, there are only two changes compared to single:

  • toselectLabel setmultipleProperties fortrue
  • selectThe labelvalueThe binding value is an array

Dull here also to write a small case:

class SelectComponent extends React.Component {
  constructor(props) {
    super(props);
    // this.state = { value: 'cute' };
    this.state = { value: ['cute']}; }handleChange(event) {
    console.log(event.target.value)
    const val = event.target.value;
    const oldValue = this.state.value;
    const i = this.state.value.indexOf(val);
    const newValue = i > -1 ? [...oldValue].splice(i, 1) : [...oldValue, val];
    this.setState({value: newValue});
  }

  handleSubmit(event) {
    alert('What kind of date are you dating today?' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={(e)= > this.handleSubmit(e)}>
        <label>What type of blind date are you dating today?<select multiple={true} value={this.state.value} onChange={(e)= > this.handleChange(e)}>
            <option value="sunshine">The sun</option>
            <option value="handsome">handsome</option>
            <option value="cute">lovely</option>
            <option value="reserved">High cold</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>); }}export default SelectComponent;
Copy the code

(But dull in Mac, Chrome testing this multiple selection seems to be problematic)

Dynamic form controlled component example

Above 👆 we implemented some simple controlled component cases, then play a slightly more difficult one.

Let’s take a look at our requirements:

Implement a component that automatically renders the form by passing in the following array:

(CInput represents an input box, CSelect represents a selection box)

// Determine the form structure
const formConfig = [
  {
    Component: CInput,
    label: 'name'.field: 'name'}, {Component: CSelect,
    label: 'gender'.field: 'sex'.options: [{ label: 'male'.value: 'man' }, { label: 'woman'.value: 'woman'}}]]// Determine the content of the form
this.state = {
  name: 'Lin Dull'.sex: 'man'
}
Copy the code

Effect:

That is, to implement a simple dynamic form and see the application of controlled components in it.

  • FormConfig determines the structure of the form, which defines what items will be in the form

  • This. state defines what the values of the forms are, and formConfig is linked to formConfig by the fields of the forms.

With 👆 above in mind, we can quickly outline what the dynamic form component might look like:

(Let’s call this component formComponent, focusing on the Render part.)

import React, { Component } from 'react';
import { CInput, CSelect } from './components'
export default class FormComponent extends Component {
	constructor (props) {
    super(props);
    this.state = {
      name: 'Lin Dull'.sex: 'man'
    }
  }
  formConfig = [
    {
      Component: CInput,
      label: 'name'.field: 'name'}, {Component: CSelect,
      label: 'gender'.field: 'sex'.options: [{ label: 'male'.value: 'man' }, { label: 'woman'.value: 'woman' }]
    }
  ]
  render () { // That's the point
    return (
      <form style={{marginTop: '50px'}} >{this.formconfig. map((item) => {// enumerate formConfig const {Type, field, name} = item; const ControlComponent = item.Component; // Extract Component return (<ControlComponent key={field} />)})}</form>)}}Copy the code

As you can see in the Render section we do a few things:

  • The enumerationformConfigAn array of
  • I’m going to extract what’s in each of these termsComponentAssigned toControlComponent
  • Render each itemComponent

The ControlComponent variable tells React which component to render. If item.componentis CInput, the final render will be
.

This ensures that we can render every form item in the formConfig array, but these form items are not under our control right now. We need to associate each ControlComponent with the value and onChange we learned earlier, like this:

<ControlComponent
  key={field}
  name={field}
  value={this.state[field]}
  onChange={this.onChange} {... item} />Copy the code

We set this.state[field] to value and this.onChange to the onChange property. (Conceivably, the CInput and CSelect components can use this.props to receive properties passed in, such as this.props. Value)

The value is determined by this.state[field]. If field is sex, value will be man.

So let’s see how the onChange method works:

onChange = (event, field) = > {
  const target = event.target;
  this.setState({
    [field]: target.value
  })
}
Copy the code

This method is also very simple, it takes an Event and field, in the event can get the user input/selection of the value.

Ok 👌, let’s take a quick look at how CInput and CSelect are implemented:

components/CInput.jsx:

import React, { Component } from 'react';

export default class CInput extends Component {
  constructor (props) {
    super(props);
  }
  render () {
    const { name, field, value, onChange } = this.props;
    return (
      <>
        <label>
          {name}
        </label>
        <input name={field} value={value} onChange={(e)= > onChange(e, field)} />
      </>)}}Copy the code

components/CSelect.jsx:

import React, { Component } from 'react';

export default class CSelect extends Component {
  constructor (props) {
    super(props);
  }
  render () {
    const { name, field, options, value, onChange } = this.props;
    return (
      <>
        <label>
          {name}
        </label>
        <select name={field} value={value} onChange={(e)= > onChange(e, field)}>
          {options.length>0 && options.map(option => {
            return <option key={option.value} value={option.value}>{option.label}</option>
          })}
        </select>
      </>)}}Copy the code

Of course, this is just a simple dynamic form implementation, which is much more complicated if you want to implement it in a project.

Uncontrolled component

For controlled components, we need to write an event handler (such as this.setState({username:)) for each state update (such as this.state.username). E. arget. Value})).

Then there’s the scenario where we just want to get the value of a form element, and we don’t care how it changes. What can we do about this scenario 🤔️?

Well… The input tag is actually a DOM element, so can we get the value of the form element the same way we get the DOM element information? So you use ref.

Take the following example 👇 :

import React, { Component } from 'react';

export class UnControll extends Component {
  constructor (props) {
    super(props);
    this.inputRef = React.createRef();
  }
  handleSubmit = (e) = > {
    console.log('We can get the value inside the input to be'.this.inputRef.current.value);
    e.preventDefault();
  }
  render () {
    return (
      <form onSubmit={e= > this.handleSubmit(e)}>
        <input defaultValue="lindaidai" ref={this.inputRef} />
        <input type="submit" value="Submit" />
      </form>)}}Copy the code

After entering the content in the input field and clicking the submit button, we can use this.inputref to successfully retrieve the DOM attributes of the input, including the user-entered values, so that we do not need to maintain a separate state for each form element as the controlled component does.

We can also use the defaultValue attribute to specify a defaultValue for a form element.

Special file tag

Another special case in input is the form control of type file.

It is always an uncontrolled component for a form control of type File because its value can only be set by the user, not programmatically.

For example, I now want to control it with status updates:

import React, { Component } from 'react';

export default class UnControll extends Component {
  constructor (props) {
    super(props);
    this.state = {
      files: []
    }
  }
  handleSubmit = (e) = > {
    e.preventDefault();
  }
  handleFile = (e) = > {
    console.log(e.target.files);
    const files = [...e.target.files];
    console.log(files);
    this.setState({
      files
    })
  }
  render () {
    return (
      <form onSubmit={e= > this.handleSubmit(e)}>
        <input type="file" value={this.state.files} onChange={(e)= > this.handleFile(e)} />
        <input type="submit" value="Submit" />
      </form>)}}Copy the code

After selecting the file, I tried to update it with setState and got an error:

So we should use the uncontrolled component method to get its value like this:

import React, { Component } from 'react';

export default class FileComponent extends Component {
  constructor (props) {
    super(props);
    this.fileRef = React.createRef();
  }
  handleSubmit = (e) = > {
    console.log('We can get the value of file as'.this.fileRef.current.files);
    e.preventDefault();
  }
  render () {
    return (
      <form onSubmit={e= > this.handleSubmit(e)}>
        <input type="file" ref={this.fileRef} />
        <input type="submit" value="Submit" />
      </form>)}}Copy the code

The files obtained here is an array. Of course, if you don’t have multiple selection enabled, the array length is always 1.

<input type="file" multiple ref={this.fileRef} />
Copy the code

OK, I believe you have a clear idea of these two groups of concepts. What? You ask me the actual application scenario?

Well… Most of the time it is recommended to use a controlled component to implement forms, because the React component handles the form data. Of course, if you select a managed component, the form data is handled by the DOM itself.

In addition, when learning both, Dull also found some better articles written, more in-depth than this one, recommended to everyone hey:

  • How to flexibly use Controlled and Uncontrolled Components in Real Business
  • Thoughts on Controlled Components

After the language

You wish the world, I wish you no bugs. So much for this article.

Mainly to introduce to you about the control and non-control components of the difference and usage, dull also finally “resume business” to write up the article, and today’s unexpected discovery of nuggets in the middle of the summary unexpectedly listed, thank you again for your love ❤️, ha ha, I believe that you will be better and better!

Like “Lin dull” guy also hope to pay attention to Lin dull public number LinDaiDai

(Because recently can not stick two-dimensional code, so grievance everyone…)

I will update some front-end knowledge content and my original article 🎉 from time to time

Your encouragement is the main motivation for my continuous creation 😊.

Related recommendations:

The most detailed BPMN.js textbook in the whole Web

If you don’t understand Babel, I’ll send you a mask.

“[Suggested stars] To come to 45 Promise interview questions a cool end (1.1W words carefully arranged)”

“[suggestion 👍] 40 more this interview questions sour cool continue (1.2W word by hand)”

“[why not three even] more simple than inheritance of JS inheritance – packaging (small test)”

【 Why not three times 】 finish these 48 questions to thoroughly understand JS inheritance (1.7W words including hot finishing – back to nature)

The dull recent interview 128 summary (containing more detailed answer) | nuggets technical essay”