Skip to main content

Difference Between Controlled and Uncontrolled Components

· 10 min read
Deniz Colak

Are you tired of managing the state of your React components on your own? Look no further than controlled components. Controlled components in React allow you to easily manage the state and behavior of your components by having the parent component take control. This not only makes your code more predictable and easy to debug, but also allows for efficient management of state in larger projects. In contrast, uncontrolled components rely on managing their own state internally, which can lead to confusion and difficulty in tracking changes. Dive deeper into the world of controlled components and learn how to harness the power of React's state management capabilities in your next project.

What is the Difference Between a Controlled and Uncontrolled Component in React?

In React, a controlled component is a component that is controlled by React state, while an uncontrolled component is a component that maintains its own internal state.

A controlled component receives its current value and an update callback via props, and the parent component manages the state of the component. When the user interacts with the component, the parent component updates the state, which in turn updates the component's value.

An uncontrolled component, maintains its own internal state, and when the user interacts with the component, it updates its own state, which in turn updates the component's value.

FeaturesControlled ComponentUncontrolled Component
Value ManagementManaged by React stateManaged by component's own internal state
User InteractionParent component updates state on user interactionComponent updates own internal state on user interaction
Data FlowData flows from parent component to componentData flows within the component
DebuggingEasier to debugMore difficult to debug
PerformanceGenerally faster as there's less state managementGenerally slower as there's more state management
Code ComplexityLess complex codeMore complex code
Best PracticesConsidered a best practiceConsidered an alternate approach

Understanding the Basics of Controlled and Uncontrolled Components in React

In React, controlled components refer to components that have their state and behavior controlled by the parent component. These components rely on props passed down from the parent component to update their state and behavior. Uncontrolled components refer to components that manage their own state internally. They use a ref to access the DOM element's current value and update the state accordingly.

A controlled functional component is a component that receives its current value and update callback via props. For example, consider a simple form that accepts an email address and a password:

const ControlledInput = ({ value, onChange }) => (
<input value={value} onChange={(e) => onChange(e.target.value)} />
);

const LoginForm = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");

return (
<form>
<ControlledInput value={email} onChange={setEmail} placeholder="Email" />
<ControlledInput
value={password}
onChange={setPassword}
placeholder="Password"
/>
<button>Submit</button>
</form>
);
};

In this example, the ControlledInput component receives its current value and an onChange callback via props. The LoginForm component maintains the state of email and password, and when the user types into the inputs, it calls the onChange callback and updates the state, which in turn updates the input values.

An uncontrolled component in React is a component that does not store its form data in its internal state. Instead, it lets the DOM handle the form data directly. Here is an example:

import React, { useRef } from "react";

const UncontrolledInput = ({ placeholder }) => {
return <input defaultValue="" placeholder={placeholder} />;
};

const LoginForm = () => {
const emailRef = useRef();
const passwordRef = useRef();

const handleSubmit = (event) => {
event.preventDefault();

const email = emailRef.current.value;
const password = passwordRef.current.value;

console.log("Email:", email);
console.log("Password:", password);
};

return (
<form onSubmit={handleSubmit}>
<UncontrolledInput ref={emailRef} placeholder="Email" />
<UncontrolledInput ref={passwordRef} placeholder="Password" />
<button type="submit">Submit</button>
</form>
);
};

In this example, UncontrolledInput using the useRef hook to create references (emailRef and passwordRef) to the input fields. When the form is submitted, we can access the values of the input fields directly through these refs.

In real-world example, in a payment system, you would use a controlled component to handle the form inputs where user enter the card information, so that the state of the form data is managed by the parent component and it can be sent to the server for processing. An uncontrolled component could be used for something like a search bar, where the search results are displayed below the bar, and the state of the search bar is managed internally by the component.

Managing State in Controlled vs Uncontrolled Components

In controlled components, the parent component is responsible for managing the state and passing it down as props to the controlled component. This makes it easy to track the state of the component and predict how it will behave. However, in larger projects with many levels of nested components, this can make the code more complex and harder to manage.

On the other hand, uncontrolled components manage their own state using a ref to access the DOM element's current value and update the state accordingly. This makes them simpler to implement, but they can be more difficult to track and manage in larger projects.

The Pros and Cons of Using Controlled vs Uncontrolled Components

Controlled components have the advantage of being more predictable and easier to debug because the state is controlled by the parent component. However, this can also make them more complex to implement in larger projects with many levels of nested components.

On the other hand, uncontrolled components are simpler to implement, but they can be more difficult to track and manage in larger projects. They are also harder to test as the internal state is not exposed.

Real-World Examples of Implementing Controlled and Uncontrolled Components in React

A common example of a controlled component is a form input field, where the state of the input is controlled by the parent component and passed down as props.

For example, consider a login form that takes the user's email and password as input. The parent component would handle the state of the input fields and pass it down as props to the controlled input fields.

import React, { useState } from "react";

const LoginForm = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");

const handleSubmit = (event) => {
event.preventDefault();
// logic to handle the form submission
};

return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>

<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>

<button type="submit">Submit</button>
</form>
);
};

export default LoginForm;

In this example, the email and password state values are controlled by the parent component LoginForm. The input fields are updated when the values of email and password change, and their values are passed down as props to the value attribute of the input fields.

An example of an uncontrolled component is a modal window, where the state of the modal (open or closed) is managed internally by the component itself. Here, the modal component uses a ref to access the DOM element and update the state accordingly.

import React, { useState, useRef } from "react";

const Modal = () => {
const [isOpen, setIsOpen] = useState(false);
const modalRef = useRef(null);

const handleClickOutside = (event) => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
setIsOpen(false);
}
};

const handleOpenModal = () => {
setIsOpen(true);
};

const handleCloseModal = () => {
setIsOpen(false);
};

return (
<div>
<button type="button" onClick={handleOpenModal}>
Open Modal
</button>
{isOpen && (
<div ref={modalRef}>
<button type="button" onClick={handleCloseModal}>
Close Modal
</button>
<p>Modal Content</p>
</div>
)}
</div>
);
};

export default Modal;

In this example, the state of the modal (open or closed) is managed internally by the component Modal. The component uses a ref to access the DOM element and handle the opening and closing of the modal. The component updates the state of isOpen when the user clicks the "Open Modal" or "Close Modal" buttons.

Another example is the handling of the state of a checkbox using controlled and uncontrolled component. A controlled checkbox component would look like this:

import React, { useState } from "react";

const ControlledCheckbox = ({ checked, onChange }) => {
return <input type="checkbox" checked={checked} onChange={onChange} />;
};

const ParentComponent = () => {
const [isChecked, setIsChecked] = useState(false);

const handleCheckboxChange = (event) => {
setIsChecked(event.target.checked);
};

return (
<ControlledCheckbox checked={isChecked} onChange={handleCheckboxChange} />
);
};

export default ParentComponent;

In this example, the parent component ParentComponent handles the state of the checkbox and passes it down as props to the controlled checkbox component ControlledCheckbox. The state of the checkbox is updated when the user interacts with it and the onChange prop is triggered, updating the state of isChecked in the parent component.

And here's an example of an uncontrolled checkbox using a functional component in React:

import React, { useRef } from "react";

const UncontrolledCheckbox = () => {
const checkboxRef = useRef(null);

const handleSubmit = () => {
console.log(checkboxRef.current.checked);
};

return (
<div>
<input type="checkbox" ref={checkboxRef} />
<button type="button" onClick={handleSubmit}>
Submit
</button>
</div>
);
};

export default UncontrolledCheckbox;

In this example, the checkbox component UncontrolledCheckbox accesses the DOM element using a ref and updates its state internally. The component logs the state of the checkbox when the user clicks the "Submit" button.

In summary, controlled and uncontrolled components have different ways of handling the state of a component, and it's important to understand when it's more appropriate to use one or the other. Depending on the size of the project, testing and maintenance needs, the choice should be made wisely.

Best Practices for Choosing Between Controlled and Uncontrolled Components in React

When deciding whether to use a controlled or uncontrolled component, it's important to consider the complexity of the component and the size of the project. If the component is simple and the project is small, an uncontrolled component may be more appropriate. However, for larger projects and more complex components, a controlled component may be easier to manage and debug.

Another factor to consider is testing. Controlled components are easier to test as their state is exposed and can be manipulated directly. Uncontrolled components, on the other hand, have internal state that is not exposed, making them harder to test.

Troubleshooting Common Issues with Controlled and Uncontrolled Components in React

One common issue with uncontrolled components is that they may not update correctly when the state changes, because they are not directly connected to the parent component's state. To fix this, make sure to use the setState method to update the state and ensure that the component re-renders.

With controlled components, a common issue is that the state may not be passed down correctly as props, resulting in the component not updating correctly. To fix this, make sure that the state is correctly passed down as props and that the component is correctly receiving the updated props.

Conclusion

In conclusion, controlled and uncontrolled components both have their own set of advantages and disadvantages, and the choice between them depends on the specific requirements of the project. Understanding the basics of controlled and uncontrolled components in React, managing state in controlled vs uncontrolled components, the pros and cons of using controlled vs uncontrolled components, real-world examples of implementing controlled and uncontrolled components in React, best practices for choosing between controlled and uncontrolled components in React, and troubleshooting common issues with controlled and uncontrolled components in React are all important for building robust and maintainable React applications.