Last updated: Apr 6, 2024
Reading timeยท3 min
The warning "A component is changing an uncontrolled input to be controlled"
occurs when an input value is initialized to undefined
but is later changed to
a different value.
To fix the warning, initialize the input value to an empty string, e.g.
value={message || ''}
.
Here is an example of how the error occurs.
import {useState} from 'react'; const App = () => { // ๐๏ธ didn't provide an initial value for `message` const [message, setMessage] = useState(); const handleChange = event => { setMessage(event.target.value); }; // โ๏ธ Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. return ( <div> <input type="text" id="message" name="message" onChange={handleChange} value={message} /> </div> ); }; export default App;
The issue is that the message
state variable is initially set to undefined
because we didn't pass an initial value for it to the
useState hook.
undefined
One way to solve the error is to provide a fallback if the input value is
undefined
.
import {useState} from 'react'; const App = () => { const [message, setMessage] = useState(); const handleChange = event => { setMessage(event.target.value); console.log(event.target.value); }; // ๐๏ธ value={message || ''} return ( <div> <input type="text" id="message" name="message" onChange={handleChange} value={message || ''} /> </div> ); }; export default App;
We used the
logical OR (||)
operator, which returns the value to the right if the value to the left is falsy
(e.g. undefined
).
message
variable stores an undefined
value, we return an empty string as a fallback.useState
hookAn alternative solution is to
pass an initial value for the state
variable to the useState
hook.
import {useState} from 'react'; const App = () => { // ๐๏ธ Pass an initial value to the useState hook const [message, setMessage] = useState(''); const handleChange = event => { setMessage(event.target.value); console.log(event.target.value); }; return ( <div> <input type="text" id="message" name="message" onChange={handleChange} value={message} /> </div> ); }; export default App;
Passing an initial value to the useState
hook gets rid of the warning because
now the message
variable doesn't change from undefined
to having a value.
The warning is caused because when the message
variable is initialized without
a value, it is set to undefined
.
Passing a prop like value={undefined}
is the same as not passing the value
prop at all.
value
prop is now passed to the input
field and the input changes from uncontrolled to controlled which is not allowed.defaultValue
prop for uncontrolled componentsNote that if you use an uncontrolled input field, you should use the
defaultValue
prop instead of value
.
import {useRef} from 'react'; const App = () => { const inputRef = useRef(null); function handleClick() { console.log(inputRef.current.value); } return ( <div> <input ref={inputRef} type="text" id="message" name="message" defaultValue="Initial value" /> <button onClick={handleClick}>Log message</button> </div> ); }; export default App;
The example above uses an uncontrolled input. Notice that the input field
doesn't have an onChange
prop or value
set.
defaultValue
prop. However, this is not necessary and you can omit the prop if you don't want to set an initial value.When using uncontrolled input fields, we access the input using a ref.
Every time the user clicks on the button in the example, the value of the uncontrolled input gets logged.
You shouldn't set the value
prop on an uncontrolled input (an input field that
doesn't have an onChange
handler) because that would make the input field
immutable and you wouldn't be able to type in it.