Search code examples
javascriptreactjsvirtual-dom

Why uncontrolled input elements lose inputs during reconciliation?


Below is the sample code that display a checkbox to wrap content, an text input field, and a counter with a button to increment

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0,
      wrapContent: false
    }
  }

  handleAdd = () => {
    this.setState({ counter: this.state.counter + 1 });
  }

  toggleWrap = () => {
    this.setState(state => state.wrapContent = !state.wrapContent);
  }

  wrapContent(content) {
    return this.state.wrapContent ?
      <div className="bg-secondary p-2">
        <div className="bg-light">{content}</div>
      </div>
      : content;
  }

  render() {
    return this.wrapContent(
      <React.Fragment>
        <input type="checkbox" checked={this.state.wrapContent} onChange={this.toggleWrap} />
        <input className="form-control"/>  {/* <--------value disappear after toggling */}
        <DisplayCounter counter={this.state.counter} />
        <div className="text-center">
          <button className="btn btn-primary" onClick={this.handleAdd}>Increment</button>
        </div>
      </React.Fragment>
    );
  }
}

class DisplayCounter extends Component {

  render() {
    return <React.Fragment><div>
      <h2>{this.props.counter}</h2>
    </div></React.Fragment>
  }
}

If I enter some text in the input, click Add button a couple of time and then toggle the checkbox, the value of the input is gone while the value of DisplayCounter still persists.

What I don't understand is why the input lose its value, isn't that react use virtual dom to minimize the number of DOM updates, so if virtual dom works for DisplayCounter, why it doesn't work for the uncontrolled form element like input?

understand why uncontrolled input elements lose its input?


Solution

  • The input there is an uncontrolled input. It keeps the data in itself. When it unmounts, the data gets erased together with him. The next time it appears, it is a fresh new input.

    DisplayCounter keeps its value, because it is a controlled component, you keep its value in state this.state.counter