React: Controlled VS Uncontrolled Components

Understanding the difference between controlled and uncontrolled React components.

Even though React’s team officially recommends implementing forms using controlled components — that’s still a choice that needs to be made by the developer implementing the form, after considering the context and purpose of it. There's no one right answer for all cases.

That’s something to keep in mind when publishing your own reusable components to cloud component hubs (like Bit.dev).

Make sure to build your shared components in a way that enables others to make that decision for themselves — expose an API that enables that and write example codes that demonstrate both methods (in the case of Bit.dev, that would be in the component page).

React components published on Bit.dev (a cloud component hub)

Controlled components

A component is controlled when you allow React to control it for you. This is a bit too generic, so let me explain:

Although this mainly applies to form elements, you can extrapolate and apply the concepts to normal HTML elements alone.

Essentially, when you’re building a component dealing with user input, you’ll be using form elements such as input , select and even textarea . They all keep an internal state that updates everytime the user provides some input. In turn, your components wrapping these elements will also keep the current input date into their state and update it through calls to the setState method.

By linking the internal state of your HTML elements with the state of your components, you’re turning the latter into what the official documentation for React calls “a single source of truth”. That means the framework gets to choose when and how to render these components based on its internal state and updates it everytime there is a change.

Take a look at the following example:

That component is quite simple, but it proves a point: React is the one in charge of controlling when and how to render it based on the internal logic of the setState method. We don’t need to worry about anything else.

This is the recommended way to write components (be it by using classes or functional components, just make sure they’re controlled components) mainly because all the benefits of the framework as applied to them. You get an abstraction layer on top of the DOM that lets you react and interact with elements without having to worry about low level DOM API methods (which as you’re about to see, are required for uncontrolled components).

Uncontrolled components

Now, on the other hand, an uncontrolled component is one that does not rely on an internal state in order to tether to the HTML element (or elements) that are a part of it.

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

Let me explain by showing you a simple example:

The above component does exactly the same as the previous example, it updates the content of a p element while you, 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 univeral language (sort of speak) that any other JS-based browser library will understand.

Testing Controlled components vs Testing Uncontrolled components

Here is a very good example of how things get a lot more complicated if you decide to go with the uncontrolled components.

Afterall, like I mentioned, going down that path is like deciding to write your web server using C instead of Node.js, just because the first will give you a lot more flexibility over how you implement your features. I mean, it’s definitely true, but at the same time, you’ll have to write a heck of a lot more code to achieve the same thing.

Now, back to the examples from above. Writing a simple test for the controlled component I showed above looks like this:

I’m using Enzyme for these tests by the way, it’s a great library for writing tests for your react components, check it out if you haven’t.

Reading the example, as you can see, all I do is shallow render my component, simulate a change event on the input field rendered and then just check the value of my internal state. That’s all the component does and all I need to check for.

On the other hand, since I don’t have an internal state for my uncontrolled component, I need to check the actual value of the affected element (the p elemenent). Here is how you can do that:

The test itself is not that much longer than the previous one, but the logics behind it are quite different. Instead of worrying about the component itself, you need to understand the internal structure of the component, and how the data needs to flow from a trigger (in this case, a change event) into the final destination (in this case, the innerHTML property of the p element).

When to use which?

Now that we’ve answered the question of what are these types of components, when will you use them?

Or actually, maybe the question should be: when or why would you go the uncontrolled route?

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

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

So yes, there is a valid use case for uncontrolled components, and yes, if you need to add them to your app, they’ll be harder to test and harder to maintain, but you have the option available which is the great thing about React.

Conclusion

Controlled components let you work with them using React’s internal state, while uncontrolled components require you to use a lower level API to achieve the same results.

Ideally, you’ll want to focus on the good old controlled components unless you need to do something weird with other libraries.

Have you had to use uncontrolled components in the past? What was the reason for that? Share your experience down in the comments!

And see you on the next one!

Learn More