Mastering ReactJS: Understanding Hooks, Components, and Libraries

Controlled and Uncontrolled Components in ReactJS

Controlled and Uncontrolled Components in ReactJS

1. What are Controlled Components?

A component is referred to as being "controlled" when we allow React to control it for us. This concept mainly applies to form elements, but we can extrapolate and apply the concepts to normal HTML elements alone*.*

Essentially, when we are building a component dealing with user input, we will be using form elements such as "input", "select" and even "textarea". These elements maintain an internal state which updates each time the user provides some input. In turn, the components wrapping these elements will also keep the current input date in their state and update it through calls to the "setState" method.

By linking the internal state of the HTML elements with the state of the components, we are turning the latter into what the official documentation for React describes as "a single source of truth". That means the framework decides whether to render these components based on its internal state and updates it every time there is a change.

import React from 'react';

export default class ControlledComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {user: ''};
    this.updateInput = this.updateInput.bind(this)
  }

  updateInput(event) {
  	this.setState({
  		user: event.target.value
  	})
  } 

  render() {
    return (
      <div> 
        <input type="text" value={this.state.user} onChange={this.updateInput}/>
        <p>{this.state.user}</p>
      </div>
    );
  }
}

React is the one in charge of controlling when and how to render it based on the internal logic of the "setState" method. We do not have to worry about anything else.

Be it by using classes or functional components, this is the recommended way to write components i.e., making sure that it is a controlled component. It is mainly because all the benefits of the framework as applied to them. We get an abstraction layer on top of the DOM that lets us react and interact with elements without having to worry about low-level DOM API methods which are required in case of uncontrolled components.

2. What are Uncontrolled Components?

Speaking of the Uncontrolled Component,  it is the one that does not rely on an internal state in order to tether to the HTML element that is a part of it.

Instead, we get a direct reference to the HTML element in question, and we can make use of it however we like. We can think of an uncontrolled component as a low-level component, one that is closer to the actual vanilla JS.

It will be evident with this simple example:

import React from 'react';

export default class UnControlledComponent extends React.Component {
  constructor(props) {
    super(props);
    this.input = React.createRef()
    this.ptelement = React.createRef()
    this.updateInput = this.updateInput.bind(this)
  }

  updateInput(event) {
    this.ptelement.current.innerHTML = this.input.current.value
  } 

  render() {
    return (
      <div> 
        <input type="text" ref={this.input} onChange={this.updateInput}/>
        <p ref={this.ptelement}></p>
      </div>
    );
  }
}

The above component does exactly the same as the previous example of Controlled Component, it updates the content of a "ptelement" while the user types inside an "input" box. Nothing too fancy, but you can see how in order to achieve the same result, we had to write code that directly accesses DOM’s API (i.e we had to directly set the innerHTML property of the target element). This can get a lot more complex if what you’re trying to do requires more logic, but you get the point.

React brings a lot of benefits to the table, and the higher level of abstraction from the DOM API is one of them. So why would the framework allow you to create uncontrolled components going against everything it was designed for? Simple: to help developers integrate React with other libraries. By giving you access to the most basic API, you’re able to speak a universal language (sort of speak) that any other JS-based browser library will understand.

3. When to use them?

Now that we are aware of these types of components, we should know, when will you use them?

The truth is we are supposed to use controlled components in most of the scenarios and as much as possible. That is what the official documentation recommends and that is the easiest way for us to build reusable components. By working on the higher abstraction layer provided by React, we can worry about our actual business logic without even bothering about the internal operations of the DOM API. This works for writing the actual application, and the associated unit tests and it even decreases the cognitive load required to mentally parse the code.

On the other hand, although their use is not recommended when writing a regular React application, if we are working on something else, maybe interacting with other libraries that do not follow React’s design pattern then relying on a lower-level layer, like the DOM API will help us speak the same language, afterwall, React, Vue, Angular and any other front-end framework, down at their code, they’re all speaking it.

Therefore, there is a valid use case for uncontrolled components, and if we need to add them to our application, they’ll be harder to test and harder to maintain, but we have the option available which is the great thing about React.

4. Summary

  • We understood the concepts of Controlled and Uncontrolled components along with their features and explanation.
  • Controlled components allow the users to work with them using React’s internal state, while the Uncontrolled Components require the users to use a lower level API to achieve the same results.
  • We also saw a few examples to further clarify the concepts.
  • In most of the scenarios, the users should focus on the good old controlled components unless they need to do something weird with the other libraries.