VDone Demo VDone Demo
Home
  • Articles

    • JavaScript
  • Study Notes

    • JavaScript Tutorial
    • Professional JavaScript
    • ES6 Tutorial
    • Vue
    • React
    • TypeScript: Build Axios from Scratch
    • Git
    • TypeScript
    • JS Design Patterns
  • HTML
  • CSS
  • Technical Docs
  • GitHub Tips
  • Node.js
  • Blog Setup
  • Learning
  • Interviews
  • Miscellaneous
  • Practical Tips
  • Friends
About
Bookmarks
  • Categories
  • Tags
  • Archives
GitHub (opens new window)

Nikolay Tuzov

Backend Developer
Home
  • Articles

    • JavaScript
  • Study Notes

    • JavaScript Tutorial
    • Professional JavaScript
    • ES6 Tutorial
    • Vue
    • React
    • TypeScript: Build Axios from Scratch
    • Git
    • TypeScript
    • JS Design Patterns
  • HTML
  • CSS
  • Technical Docs
  • GitHub Tips
  • Node.js
  • Blog Setup
  • Learning
  • Interviews
  • Miscellaneous
  • Practical Tips
  • Friends
About
Bookmarks
  • Categories
  • Tags
  • Archives
GitHub (opens new window)
  • 核心概念

    • Introduction to JSX
    • Rendering Elements
    • Components & Props
    • State & Lifecycle
    • Handling Events
    • Conditional Rendering
    • Lists & Keys
    • Forms
      • Controlled Components (Two-Way Data Binding)
      • The textarea Tag (Using value to Define Content)
      • The select Tag (value Replaces the selected Attribute)
        • Multi-Select
        • Summary
      • The File Input Tag (Uncontrolled Component)
      • Handling Multiple Inputs
      • Controlled Input Null Value
      • Alternatives to Controlled Components (Uncontrolled Components)
      • Fully-Fledged Solutions
    • Lifting State Up (Shared State)
    • Composition vs Inheritance
    • Thinking in React
  • 高级指引

  • Hook

  • 案例演示

  • 《React》笔记
  • 核心概念
xugaoyi
2021-03-24
Contents

Forms

# 08. Forms

# Controlled Components (Two-Way Data Binding)

In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState() (opens new window).

We can combine the two by making the React state be the "single source of truth". Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a "controlled component".

For example, if we want the previous example to log the name when it is submitted, we can write the form as a controlled component:

class Name extends React.Component {
  constructor(props){
    super(props);
    this.state = {value: ''}

    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  handleChange(event){
    this.setState({value: event.target.value})
  }

  handleSubmit(event){
    alert(this.state.value)
    event.preventDefault();
  }

  render(){
    return(
     <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

Similar to two-way binding in Vue, like v-model.

# The textarea Tag (Using value to Define Content)

In HTML, a <textarea> element defines its text by its children:

<textarea>
  Hello, this is some text in a text area
</textarea>
1
2
3

In React, a <textarea> uses a value attribute instead. This makes it very similar to a form that uses a single-line input:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }
  handleSubmit(event) {
    alert('Submitted essay: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Note that this.state.value is initialized in the constructor, so the text area starts with default text.

# The select Tag (value Replaces the selected Attribute)

React uses a value attribute on the root select tag instead of the selected attribute on option elements.

In HTML, <select> creates a drop-down list. For example, this HTML creates a drop-down list of fruits:

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>
1
2
3
4
5
6

Note that the Coconut option is initially selected because of the selected attribute. React, instead of using the selected attribute, uses a value attribute on the root select tag. This is more convenient in a controlled component because you only need to update it in one place. For example:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# Multi-Select

You can pass an array into the value attribute, allowing you to select multiple options in a select tag:

<select multiple={true} value={['B', 'C']}>
1

# Summary

Overall, this makes <input type="text">, <textarea>, and <select> all work very similarly -- they all accept a value attribute that you can use to implement a controlled component.

# The File Input Tag (Uncontrolled Component)

In HTML, an <input type="file"> lets the user choose one or more files from their storage device to be uploaded to a server, or manipulated by JavaScript via the File API (opens new window).

<input type="file" />
1

Because its value is read-only, it is an uncontrolled component in React. It will be discussed together with other uncontrolled components later in the documentation (opens new window).

# Handling Multiple Inputs

When you need to handle multiple input elements, you can add a name attribute to each element and let the handler function choose what to do based on the value of event.target.name.

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      // ES6 computed property name
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# Controlled Input Null Value

Specifying the value prop on a controlled component (opens new window) prevents the user from changing the input unless you desire so. If you've specified a value but the input is still editable, you may have accidentally set value to undefined or null.

The following code demonstrates this. (The input is locked at first but becomes editable after a short delay.)

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
1
2
3
4
5

# Alternatives to Controlled Components (Uncontrolled Components)

It can sometimes be tedious to use controlled components, because you need to write an event handler for every way your data can change and pipe all of the input state through a React component. This can become particularly annoying when you are converting a preexisting codebase to React, or integrating a React application with a non-React library. In these situations, you might want to check out uncontrolled components (opens new window), an alternative technique for implementing input forms.

# Fully-Fledged Solutions

If you're looking for a complete solution including validation, keeping track of the visited fields, and handling form submission, Formik (opens new window) is one of the popular choices. However, it is built on the same principles of controlled components and managing state -- so don't neglect to learn them.

Edit (opens new window)
#React
Last Updated: 2026/03/21, 12:14:36
Lists & Keys
Lifting State Up (Shared State)

← Lists & Keys Lifting State Up (Shared State)→

Recent Updates
01
How I Discovered Disposable Email — A True Story
06-12
02
Animations in Grid Layout
09-15
03
Renaming a Git Branch
08-11
More Articles >
Theme by VDone | Copyright © 2026-2026 Nikolay Tuzov | MIT License | Telegram
  • Auto
  • Light Mode
  • Dark Mode
  • Reading Mode