Skip to content

Controlled vs Uncontrolled Components

In React, there are two ways of handling form data input: controlled vs uncontrolled.

Controlled

In a controlled component, we manage the data explicitly either in a local state or reducer.

js
import React, { useState } from 'react';

function Controlled() {
	const [value, setValue] = useState('');

	const handleChange = (event) => {
		setValue(event.target.value);
	};

	return (
		<input type="text" value={value} onChange={handleChange} />
	);
}

Advantages include:

  • Single source of truth: Easier to synchronize the UI with a single state.
  • Validation: Easy to control the validation of the data.

Disadvantages include:

  • Performance: The state is updated by the component on every input.
  • Complexity: Loses many built in features on typing the input and would need to manually create a good UX.

Uncontrolled

In an uncontrolled component, the input element manages the data itself and we use ref to get the data implicitly.

js
import React, { useRef } from 'react';

function Uncontrolled() {
	const inputRef = useRef(null);

const handleSubmit = (e) => { 
		e.preventDefault();
		console.log(`Submitted value: ${inputRef.current.value}`);
	}

	return (
		<form onSubmit={handleSubmit}>
			<input type="text" ref={inputRef} />
		</form>
	);
}

Advantages include:

  • Less Code: The default built-in input behaviors handle many common data input expectations.
  • Performance: Requires fewer rendering of the component.

Disadvantages include:

  • Validation: Harder to validate.
  • Synchronization: Harder to share data value across different components.

Suggestion

In practice, I would recommend defaulting to uncontrolled input while still storing the state onChange for its simplicity and input standardization:

js
function onChange(e) {
	setState(e.target.value)
}

<input onChange={onChange}/>

Only switch to controlled input when we need full control over the data of the input.