A component is changing an uncontrolled input of type text to be controlled error in ReactJS
The reason is, in state you defined:
this.state = { fields: {} }
fields as a blank object, so during the first rendering this.state.fields.name
will be undefined
, and the input field will get its value as:
value={undefined}
Because of that, the input field will become uncontrolled.
Once you enter any value in input, fields
in state gets changed to:
this.state = { fields: {name: 'xyz'} }
And at that time the input field gets converted into a controlled component; that's why you are getting the error:
A component is changing an uncontrolled input of type text to be
controlled.
Possible Solutions:
1- Define the fields
in state as:
this.state = { fields: {name: ''} }
2- Or define the value property by using Short-circuit evaluation like this:
value={this.state.fields.name || ''} // (undefined || '') = ''
Getting A component is changing an uncontrolled input to be controlled (...) when using KeyboardDatePicker
This is because the period value changes from an empty string to a Date
, in your initial value, you should also pass a Date
.
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. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
Interface update
export interface Period {
startDate: Date;
endDate: Date;
}
Initial state update
const [periodState, setPeriodState] = useState<Period>({
startDate: new Date(),
endDate: new Date()
});
(ReactJS) Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined ...
I've updated your component with this working example.
const { useEffect, useState } = React;
function Example({ user }) {
const [name, setName] = useState(user.name);
function handleChange(e) {
setName(e.target.value);
}
useEffect(() => console.log(name), [name]);
if(!user) return <div></div>;
return (
<form>
<fieldset>
<legend>Edit name</legend>
<input
id="name"
value={name}
onChange={handleChange}
/>
</fieldset>
</form>
)
};
const user = { name: 'Bob' };
ReactDOM.render(
<Example user={user} />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Warning: A component is changing an uncontrolled input to be controlled. Minimum working example
After adding value to TextField
error disappears:
import React, { useState } from "react";
import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField";
export default function Inputs() {
//const [gender, setGender] = useState("");
const [data, setData] = useState("");
return (
<TextField
select
value={data}
onChange={(e) => setData(e.target.value)}
>
<MenuItem key={"a"} value={"a"}>
a
</MenuItem>
</TextField>
);
}
Seems like TextField
started in uncontrolled mode.
A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined
Issues
React uses Synthetic Events that are quickly nullified and returned back to the event pool. By the time the state update is processed the event object has been nullified and your state values set to undefined. This causes the controlled to uncontrolled input warning.
The other issue is not correctly shallow copying the existing nested
contactUs
state when new values arrive for other inputs. React'sthis.setState
only merges root level state, all deeper state should be manually merged.
Solution
Save the input value before enqueueing the state update. Use a functional state update to access the previous state.
handleFirstNameChange = (event) => {
const { value } = event.target;
this.setState(prevState => ({
contactUs: {
...prevState.contactUs, // <-- copy other nested state
name: value,
},
}));
};
Suggestion
To make your code a bit more DRY you can define a single onChange
handler to handle all the inputs.
handleChange = (event) => {
const { id, value } = event.target;
this.setState((prevState) => ({
contactUs: {
...prevState.contactUs,
[id]: value
}
}));
};
The inputs. Ensure all the id
attributes match their react state counterpart. Use the same handler for each.
<div className="form-element">
<label>Name:</label>
<input
type="text"
id="name"
value={this.state.contactUs.name}
onChange={this.handleChange}
/>
</div>
<div className="form-element">
<label>Email:</label>
<input
type="email"
id="emailAddress"
value={this.state.contactUs.emailAddress}
onChange={this.handleChange}
/>
</div>
<div className="form-element">
<label>Telephone:</label>
<input
type="text"
id="telephone"
value={this.state.contactUs.telephone}
onChange={this.handleChange}
/>
</div>
Warning: A component is changing a controlled input to be uncontrolled in React js
Unlike setting the state in a class component, useState
doesn't merge the object you pass, and you have to do it manually when setting the state. When you set age
, for example, you replace the entire state object, which makes name
to be undefined
, and vice versa.
Use functional update, and create a new state based on the previous state object before setting it.
const { useState } = React
const AddUser = () => {
const initialUserState = {
id: null,
name: '',
age: 0
}
const [users, setUsers] = useState(initialUserState)
const handleChange = (e) => {
// create the new state and set it
setUsers(prev => ({ ...prev, [e.target.name]: e.target.value }))
e.preventDefault()
}
return (
<div>
<input name="name" type="text" value={users.name} onChange={handleChange}/>
<input name="age" type="number" value={users.age} onChange={handleChange}/>
</div>
)
}
ReactDOM.render(
<AddUser />,
root
)
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
React - changing an uncontrolled input
I believe my input is controlled since it has a value.
For an input to be controlled, its value must correspond to that of a state variable.
That condition is not initially met in your example because this.state.name
is not initially set. Therefore, the input is initially uncontrolled. Once the onChange
handler is triggered for the first time, this.state.name
gets set. At that point, the above condition is satisfied and the input is considered to be controlled. This transition from uncontrolled to controlled produces the error seen above.
By initializing this.state.name
in the constructor:
e.g.
this.state = { name: '' };
the input will be controlled from the start, fixing the issue. See React Controlled Components for more examples.
Unrelated to this error, you should only have one default export. Your code above has two.
Related Topics
Google Chrome Console.Log() Inconsistency with Objects and Arrays
Detecting the Onload Event of a Window Opened with Window.Open
How to Access a JavaScript Variable Using a String That Contains the Name of the Variable
How to View Events Fired on an Element in Chrome Devtools
How to Initialize an Array's Length in JavaScript
How to Use JavaScript Source Maps (.Map Files)
Fixing JavaScript Array Functions in Internet Explorer (Indexof, Foreach, etc.)
How to Replace All Dots in a String Using JavaScript
Angularjs Ng-Click Stoppropagation
What Exactly Is the Parameter E (Event) and Why Pass It to JavaScript Functions
Validation of File Extension Before Uploading File
Convert Special Characters to HTML in JavaScript
How to Define Two Angular Apps/Modules in One Page
What Is the Fastest or Most Elegant Way to Compute a Set Difference Using JavaScript Arrays
How to Fetch an Array of Urls with Promise.All
Square Brackets JavaScript Object Key
Javascript: Collision Detection
Getting the Values of All the CSS Properties of a Selected Element in Selenium