I am using React JS.
I am setting the state, however, there is a problem, in the value={} part of the input field.
Here is my code:
import React from 'react';
class SomeClass extends React.Component{
constructor(props){
super(props);
this.state = {
passCode: {
email: "Email",
password: "Password"
},
errorMessage: ''
};
}
handleChange = (event) =>{
console.log(`input detected`);
let request = Object.assign({},this.state.passCode);
request.email = event.target.value;
request.password = event.target.value;
this.setState({passCode: request});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>Email Address</label>
<input type="text" value={this.state.passCode.email} onChange={this.handleChange} placeholder="Enter Email Address" />
<label>Password</label>
<input type="password" value={this.state.passCode.password} onChange={this.handleChange} placeholder="Enter One Time Password" />
<Button type="submit">
Sign In</Button>
</form>
);
}
}
export default SomeClass;
My Question:
I have is for the value attribute of the input fields, I think setState is not working properly:
In the passCode object of the state.
When I type the email, both fields get set to what I typed in the email field.
When I type the password, both fields get set to what I typed in the password field.
What's the reason for such problem?
setState is working properly; your handleChange isn't. You're setting both email and password to the value of the input regardless of which input the change occurred on. Instead, you want to update just the relevant property. To do that, I'd add name attributes to the input elements (name="email" and name="password"), then:
handleChange = ({currentTarget: {name, value}}) =>{
console.log(`input detected`);
this.setState(({passCode}) => {
return {passCode: {...passCode, [name]: value}};
});
};
Key bits there are:
It only updates the property the change related to, keeping the other one unchanged, by using the name of the input.
It uses the callback version of setState (best practice since we're setting state based on existing state).
Your funtion handleChange is wrong. You should update like this with using name:
handleChange = (event) => {
console.log(`input detected`);
const { name, value } = event.target;
this.setState({ passCode: { ...this.state.passCode, [name]: value } });
};
And set name to input:
<input type="text" value={this.state.passCode.email} onChange={this.handleChange} placeholder="Enter Email Address" name="email" />
<input type="password" value={this.state.passCode.password} onChange={this.handleChange} placeholder="Enter One Time Password" name="password" />
The methods they gaved are good, but I still suggest that you separate the methods of changing the two, maybe it will be much better.
class EditLocation extends Component {
constructor(props) {
super();
this.state = {
LocationId: '',
locationOptions: [],
}
this.baseState = this.state;
this.findLocationById = this.findLocationById.bind(this);
}
findLocationById = (locationId) => {
let locationOptions = [];
if (locationId <= 0) {
setTimeout(()=>{
this.setState(this.baseState);
locationOptions.push(
<CustomInput
type="checkbox"
id={value.LocationTypeId}
key={value.LocationTypeId}
value={value.LocationTypeId}
defaultChecked={false}
label={`${value.LocationTypeName}`}
className="mb-0"
onChange={this.handleCheckbox.bind(this)}
/>
)
this.setState({locationOptions:locationOptions})
},200)
else {
setTimeout(() => {
let location = this.props.store.selectedLocation;
this.props.store.LocationTypes.forEach((value)=>{
if(location.LocationTypes ?
location.LocationTypes.includes(value.LocationTypeId): false)
{
locationOptions.push(
<CustomInput
type="checkbox"
id={value.LocationTypeId}
key={value.LocationTypeId}
value={value.LocationTypeId}
defaultChecked={true}
label={`${value.LocationTypeName}`}
className="mb-0"
onChange={this.handleCheckbox.bind(this)}
/>
)
}
else
{
locationOptions.push(
<CustomInput
type="checkbox"
id={value.LocationTypeId}
key={value.LocationTypeId}
value={value.LocationTypeId}
defaultChecked={false}
label={`${value.LocationTypeName}`}
className="mb-0"
onChange={this.handleCheckbox.bind(this)}
/>
)
}
})
this.setState({
LocationId: location.LocationId,
locationOptions: locationOptions,
})
render(){
return (
<div>
<Modal>
<Form>
<FormGroup>
<input
value={this.state.LocationId}
type="text"
name="Location"
id="Location"
/>
</FormGroup>
<FormGroup>
{console.log(this.state.locationOptions)} // showing updated state value
{this.state.locationOptions} // showing previous state.locationOptions value
</FormGroup>
</Form>
</Modal>
</div>
)
}
}
console.log() inside the render is updating the value by my checks on customInput are not updating. I need to either reopen the modal or reload the whole program to see updates.
Any solution and resources would be helpful as I am stuck at it for hours and can't seem to figure the issue. and store is mobx store if that helps
You using setState in a wrong way.
setState() enqueues changes to the component state and tells React
that this component and its children need to be re-rendered with the
updated state. This is the primary method you use to update the user
interface in response to event handlers and server responses.
React docs
So it asynchronous and you can't guarantee when update happens.
Using setTimeout is a bad manners in React.
Storing whole components in state, e.g. locationOptions isn't good idea either.
Better to move input to separate component, as i see only defaultChecked different.
Better to use Hooks, easier to think about this in React way, it's require some time and effort to figure out how to write declarative instead of imperative code.
Can refactor a litle
// sync local state and props is a pain, better to avoid it
//constructor(props) {
//super(props);
//const location = this.props.store.selectedLocation
// this.state = {
//LocationId: location.LocationId,
//}
//}
const location = this.props.store.selectedLocation;
render() {
return (
<div>
<Modal>
<Form>
<FormGroup>
<input
value={this.props.store.selectedLocation}
type="text"
name="Location"
id="Location"
/>
</FormGroup>
<FormGroup>
{this.props.store.cationTypes.map((value) => (
<CustomInput
type="checkbox"
id={value.LocationTypeId}
key={value.LocationTypeId}
value={value.LocationTypeId}
defaultChecked={location.LocationTypes.includes(value.LocationTypeId)}
label={`${value.LocationTypeName}`}
className="mb-0"
onChange={this.handleCheckbox.bind(this)}
/>}
))
</FormGroup>
</Form>
</Modal>
</div>
);
}
I am having trouble making a checkbox react to its onChange event(checkbox remains in its default state). I have seen similar post here, but nothing really seems to work. I'm hoping a different set of eyes could help figure out whats wrong with my logic.
Here is the handleChange function, and the initial values of the input fields which are both in the parent component
const [accountData, setAccountData]=useState({accountType:'Free', //default input value for text input
accountStatus: false }) //default input for checkbox input
const handleSubmit={/*submitLogic*/}
const handleChange = e =>{
const target = e.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
setAccountData({...accountData, [name]:value})
}
//passing the handle change as a prop to the EditView component\\
<EditView handleChange={handleChange} handleSubmit={handleSubmit} data={accountData}/>
Here is the child component that contains the input form(Concern is the checkbox, as the text input field reacts to its onChange event).
const EditView = ({ handleChange, handleSubmit, data}) =>{
const isChecked=data.accountStatus;
<form onSubmit={handleSubmit}>
<div className="form-row" >
<div className="form-group col-md-6">
< label htmlFor="accountType">Account Type:</label>
<input
type="text"
className="form-control"
id="accountType"
name="accountType"
value={data.accountType}
onChange={handleChange}/>
</div>
<div className="form-group col-md-6">
< label htmlFor="accountStatus">Account Status:</label>
<div className="custom-control custom-switch">
<input
type="checkbox"
className="custom-control-input "
id="accountStatus"
name='accountStatus'
checked={isChecked}
//value={isChecked}
onChange={handleChange}
/>
</div>
</div>
<button type='submit' >Submit</button>
</form>
}
I even tried creating a different onChange handle function, but still had the same problem. Have I done something wrong in the above code that makes my checkbox not change from its default state?
try this
onChange={event => handleChange(event)}
The value passed to useState is the initial value of the state. If you want to use a values passed down through the properties, without managing it in the component itself don't use state.
const EditView = ({handleChange, handleSubmit, data}) => {
const isChecked = data.accountStatus;
// ...
};
When you use:
const [isChecked, setIsChecked] = useState(data.accountStatus);
You are saying, I want to manage isChecked in this component, but the initial value is set to data.accountStatus. For this reason changing data.accountStatus later on won't effect the value of isChecked, since that can only be changed with the setIsChecked function.
The reason the checkbox doesn't work after the edit you made to the question is due to the use of Bootstrap. When you use custom form elements Bootstrap will replace the actual input element, with some visual representation (styled element). This means that when you click on the Bootstrap checkbox/switch you aren't actually clicking on the input.
To solve this issue I'd recommend using React Bootstrap which integrates Bootstrap into React. Here is a working example (click the "full page" button after running the snippet to prevent overlapping of the console output with the content):
const {useState} = React;
const {render} = ReactDOM;
const {Form} = ReactBootstrap;
function App() {
const [accountData, setAccountData] = useState({
accountType: "Free",
accountStatus: false,
});
const handleChange = ({target}) => {
const name = target.name;
const value = target.type == "checkbox" ? target.checked : target.value;
setAccountData({...accountData, [name]: value});
};
console.log(accountData);
return <EditView data={accountData} onChange={handleChange} />;
}
function EditView({data, onChange}) {
return (
<Form>
<Form.Group>
<Form.Label>Account Type:</Form.Label>
<Form.Control
id="accountType"
name="accountType"
type="text"
value={data.accountType}
onChange={onChange}
/>
</Form.Group>
<Form.Group>
<Form.Label>Account Status:</Form.Label>
<Form.Switch
id="accountStatus"
name="accountStatus"
label=""
checked={data.accountStatus}
onChange={onChange}
/>
</Form.Group>
</Form>
);
}
render(<App />, document.getElementById("root"));
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" />
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react-bootstrap#next/dist/react-bootstrap.min.js"></script>
<div id="root"></div>
I am a beginner with React and I have been using the NPM package react-
tabs in order to display tabs on my site.
I have been trying to allow the
user to update descriptions of their profiles using a checkbox to open an input type=text tag allowing the user to put in their own details for their profile.
However, I am struggling to set the state of the description for the tab using the handleDesc function. I am not sure how I can update the state of the descriptions for each tab. I keep getting the error "A component is changing a controlled input of type checkbox to be uncontrolled."
Can anyone help me?
class EditSite extends React.Component {
constructor(props) {
super(props);
this.state = {About: true,
"Opening Hours": true,
Contact: true,
Menu: false,
Offers: false,
"External Links": true,
tabOptions: {
About: {desc:'1'},
"Opening Hours": {desc: '2'},
Contact: {desc:'3'},
Menu: {desc:'4'},
Offers: {desc:'5'},
"External Links": {desc:'6'},
}
}
Below is the function (handleDesc) I am struggling with to set the state of the descriptions.
handleDesc = (event) => {
let tabOptions = {...this.state.tabOptions[event.target.name]};
tabOptions = event.target.value;
console.log(tabOptions);
this.setState({tabOptions: [event.target.name]});
}
render() {
const links = [];
const tabs = [];
const tabPanels = [];
The second input tag is where I would like the user to be able to add their own details to the specific tab.
Object.keys(this.state.tabOptions).forEach(name => {
links.push(
<div>
<label key={name}>{name}</label>
<input
type="checkbox"
checked={this.state[name]}
name={name}
onChange={this.handleCheckClicked}
/>
{ this.state[name] === true ? (
<input
name={name}
type='text'
onChange={this.handleDesc}
/>
) : null }
</div>
);
if (!this.state[name]) return;
const { desc } = this.state.tabOptions[name];
tabs.push(
<Tab>
<h3>{name}</h3>
</Tab>
);
tabPanels.push(
<TabPanel>
{desc}
</TabPanel>
);
});
SetState wants a new object. So settings just a property of a nested object isn’t possible in that way. What you could do is copying the tabOptions and changing the properties you want before you pass it to setState.
example
handleDesc = (event) => {
const newTabOptions = {
...this.state.tabOptions,
[event.target.name]: {desc: event.target.value}
this.setState({tabOptions: newTabOptions});
}
A quick way to resolve this would be to revise that code that binds the <input /> element's onChange handler to the following:
links.push(
<div>
<label key={name}>{name}</label>
<input
type="checkbox"
checked={this.state[name]}
name={name}
onChange={ (event) => this.handleCheckClicked(event) /* <-- update this also */ }
/>
{ this.state[name] === true ? (
<input
name={name}
type='text'
onChange={ (event) => this.handleDesc(event) /* [ UPDATE HERE ] */ }
/>
) : null }
</div>
);
By declaring an arrow function inline like this:
onChange={ (event) => this.handleDesc(event) }
it will set the context of the handleDesc function to be the <EditSite /> component (currently, the context will be the global window object). This means that when you access this.state, or call a method like this.setState() inside of the handleDesc, it will work as expected.
For more information on this subtle characteristic of "arrow functions" see this article.
Update
Also, consider updating your handleDesc method like so to correctly mutate your state:
handleDesc = (event) => {
let tabOptions = {...this.state.tabOptions[event.target.name]};
tabOptions = event.target.value;
console.log(tabOptions);
this.setState({tabOptions}); // <--- Update this line
}
Update 2
On further investigation (into react's source code), it would see the problem is that your <input /> element does not have a checked attribute when this.state[name] === true is satisfied. If you apply a dummy attribute/value pair of checked={ true } on the input rendered in the this.state[name] === true case, this warning message should stop showing:
links.push(
<div>
<label key={name}>{name}</label>
<input
type="checkbox"
checked={this.state[name]}
name={name}
onChange={this.handleCheckClicked}
/>
{ this.state[name] === true ? (
<input
name={name}
type='text'
checked={ true } /* <-- Update this line, don't include this comment in code */
onChange={this.handleDesc}
/>
) : null }
</div>
);
Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.*
Following is my code:
constructor(props) {
super(props);
this.state = {
fields: {},
errors: {}
}
this.onSubmit = this.onSubmit.bind(this);
}
....
onChange(field, e){
let fields = this.state.fields;
fields[field] = e.target.value;
this.setState({fields});
}
....
render() {
return(
<div className="form-group">
<input
value={this.state.fields["name"]}
onChange={this.onChange.bind(this, "name")}
className="form-control"
type="text"
refs="name"
placeholder="Name *"
/>
<span style={{color: "red"}}>{this.state.errors["name"]}</span>
</div>
)
}
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 || '') = ''
Changing value to defaultValue will resolve it.
Note:
defaultValue is only for the initial load.
If you want to initialize the input then you should use defaultValue, but if you want to use state to change the value then you need to use value. Read this for more.
I used value={this.state.input ||""} in input to get rid of that warning.
Inside the component put the input box in the following way.
<input className="class-name"
type= "text"
id="id-123"
value={ this.state.value || "" }
name="field-name"
placeholder="Enter Name"
/>
In addition to the accepted answer, if you're using an input of type checkbox or radio, I've found I need to null/undefined check the checked attribute as well.
<input
id={myId}
name={myName}
type="checkbox" // or "radio"
value={myStateValue || ''}
checked={someBoolean ? someBoolean : false}
/>
And if you're using TS (or Babel), you could use nullish coalescing instead of the logical OR operator:
value={myStateValue ?? ''}
checked={someBoolean ?? false}
SIMPLY, You must set initial state first
If you don't set initial state react will treat that as an uncontrolled component
that's happen because the value can not be undefined or null to resolve you can do it like this
value={ this.state.value ?? "" }
const [name, setName] = useState()
generates error as soon as you type in the text field
const [name, setName] = useState('') // <-- by putting in quotes
will fix the issue on this string example.
As mentioned above you need to set the initial state, in my case I forgot to add ' ' quotes inside setSate();
const AddUser = (props) => {
const [enteredUsername, setEnteredUsername] = useState()
const [enteredAge, setEnteredAge] = useState()
Gives the following error
Correct code is to simply set the initial state to an empty string ' '
const AddUser = (props) => {
const [enteredUsername, setEnteredUsername] = useState('')
const [enteredAge, setEnteredAge] = useState('')
Set Current State first ...this.state
Its because when you are going to assign a new state it may be undefined. so it will be fixed by setting state extracting current state also
this.setState({...this.state, field})
If there is an object in your state, you should set state as follows,
suppose you have to set username inside the user object.
this.setState({user:{...this.state.user, ['username']: username}})
Best way to fix this is to set the initial state to ''.
constructor(props) {
super(props)
this.state = {
fields: {
first_name: ''
}
}
this.onChange = this.onChange.bind(this);
}
onChange(e) {
this.setState({
fields:{
...this.state.fields,
[e.target.name]: e.target.value
}
})
}
render() {
return(
<div className="form-group">
<input
value={this.state.fields.first_name}
onChange={this.onChange}
className="form-control"
name="first_name" // Same as state key
type="text"
refs="name"
placeholder="Name *"
/>
<span style={{color: "red"}}>{this.state.errors.first_name}</span>
</div>
)
}
Then you can still run your checks like if (field) and still achieve the same result if you have the value as ''.
Now since your value is now classified as type string instead of undefined after evaluation. Thus, clearing the error from the console of a big red block 😁😎.
I am new to reactjs and I am using version 17 of reactjs
I was getting this problem
I solved:
Instead of this
const [email, setEmail] = useState();
I added this
const [email, setEmail] = useState("");
In useState function I added quotes to initialize the data and the error was gone.
Put empty value if the value does not exist or null.
value={ this.state.value || "" }
If you're setting the value attribute to an object's property and want to be sure the property is not undefined, then you can combine the nullish coalescing operator ?? with an optional chaining operator ?. as follows:
<input
value={myObject?.property ?? ''}
/>
In my case it was pretty much what Mayank Shukla's top answer says. The only detail was that my state was lacking completely the property I was defining.
For example, if you have this state:
state = {
"a" : "A",
"b" : "B",
}
If you're expanding your code, you might want to add a new prop so, someplace else in your code you might create a new property c whose value is not only undefined on the component's state but the property itself is undefined.
To solve this just make sure to add c into your state and give it a proper initial value.
e.g.,
state = {
"a" : "A",
"b" : "B",
"c" : "C", // added and initialized property!
}
Hope I was able to explain my edge case.
If you use multiple input in on field, follow:
For example:
class AddUser extends React.Component {
constructor(props){
super(props);
this.state = {
fields: { UserName: '', Password: '' }
};
}
onChangeField = event => {
let name = event.target.name;
let value = event.target.value;
this.setState(prevState => {
prevState.fields[name] = value;
return {
fields: prevState.fields
};
});
};
render() {
const { UserName, Password } = this.state.fields;
return (
<form>
<div>
<label htmlFor="UserName">UserName</label>
<input type="text"
id='UserName'
name='UserName'
value={UserName}
onChange={this.onChangeField}/>
</div>
<div>
<label htmlFor="Password">Password</label>
<input type="password"
id='Password'
name='Password'
value={Password}
onChange={this.onChangeField}/>
</div>
</form>
);
}
}
Search your problem at:
onChangeField = event => {
let name = event.target.name;
let value = event.target.value;
this.setState(prevState => {
prevState.fields[name] = value;
return {
fields: prevState.fields
};
});
};
Using React Hooks also don't forget to set the initial value.
I was using <input type='datetime-local' value={eventStart} /> and initial eventStart was like
const [eventStart, setEventStart] = useState();
instead
const [eventStart, setEventStart] = useState('');.
The empty string in parentheses is difference.
Also, if you reset form after submit like i do, again you need to set it to empty string, not just to empty parentheses.
This is just my small contribution to this topic, maybe it will help someone.
like this
value={this.state.fields && this.state.fields["name"] || ''}
work for me.
But I set initial state like this:
this.state = {
fields: [],
}
I came across the same warning using react hooks,
Although I had already initialized the initial state before as:-
const [post,setPost] = useState({title:"",body:""})
But later I was overriding a part of the predefined state object on the onChange event handler,
const onChange=(e)=>{
setPost({[e.target.name]:e.target.value})
}
Solution
I solved this by coping first the whole object of the previous state(by using spread operators) then editing on top of it,
const onChange=(e)=>{
setPost({...post,[e.target.name]:e.target.value})
}
Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
Solution : Check if value is not undefined
React / Formik / Bootstrap / TypeScript
example :
{ values?.purchaseObligation.remainingYear ?
<Input
tag={Field}
name="purchaseObligation.remainingYear"
type="text"
component="input"
/> : null
}
The reason of this problem when input field value is undefined then throw the warning from react. If you create one changeHandler for multiple input field and you want to change state with changeHandler then you need to assign previous value using by spread operator. As like my code here.
constructor(props){
super(props)
this.state = {
user:{
email:'',
password:''
}
}
}
// This handler work for every input field
changeHandler = event=>{
// Dynamically Update State when change input value
this.setState({
user:{
...this.state.user,
[event.target.name]:event.target.value
}
})
}
submitHandler = event=>{
event.preventDefault()
// Your Code Here...
}
render(){
return (
<div className="mt-5">
<form onSubmit={this.submitHandler}>
<input type="text" value={this.state.user.email} name="email" onChage={this.changeHandler} />
<input type="password" value={this.state.user.password} name="password" onChage={this.changeHandler} />
<button type="submit">Login</button>
</form>
</div>
)
}
Multiple Approch can be applied:
Class Based Approch: use local state and define existing field with default value:
constructor(props) {
super(props);
this.state = {
value:''
}
}
<input type='text'
name='firstName'
value={this.state.value}
className="col-12"
onChange={this.onChange}
placeholder='Enter First name' />
Using Hooks React > 16.8 in functional style components:
[value, setValue] = useState('');
<input type='text'
name='firstName'
value={value}
className="col-12"
onChange={this.onChange}
placeholder='Enter First name' />
If Using propTypes and providing Default Value for propTypes in case of HOC component in functional style.
HOC.propTypes = {
value : PropTypes.string
}
HOC.efaultProps = {
value: ''
}
function HOC (){
return (<input type='text'
name='firstName'
value={this.props.value}
className="col-12"
onChange={this.onChange}
placeholder='Enter First name' />)
}
Change this
const [values, setValues] = useState({intialStateValues});
for this
const [values, setValues] = useState(intialStateValues);
I also faced the same issue. The solution in my case was I missed adding 'name' attribute to the element.
<div className="col-12">
<label htmlFor="username" className="form-label">Username</label>
<div className="input-group has-validation">
<span className="input-group-text">#</span>
<input
type="text"
className="form-control"
id="username"
placeholder="Username"
required=""
value={values.username}
onChange={handleChange}
/>
<div className="invalid-feedback">
Your username is required.
</div>
</div>
</div>
After I introduced name = username in the input list of attributes it worked fine.
For functional component:
const SignIn = () => {
const [formData, setFormData] = useState({
email: "",
password: ""
});
const handleChange = (event) => {
const { value, name } = event.target;
setFormData({...formData, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("Signed in");
setFormData({
email: "",
password: ""
});
};
return (
<div className="sign-in-container">
<form onSubmit={handleSubmit}>
<FormInput
name="email"
type="email"
value={formData.email}
handleChange={handleChange}
label="email"
required
/>
<FormInput
name="password"
type="password"
value={formData.password}
handleChange={handleChange}
label="password"
required
/>
<CustomButton type="submit">Sign in</CustomButton>
</form>
</div>
);
};
export default SignIn;
While this might sound crazy, the thing that fixed this issue for me was to add an extra div. A portion of the code as an example:
... [Other code] ...
const [brokerLink, setBrokerLink] = useState('');
... [Other code] ...
return (
... [Other code] ...
<div styleName="advanced-form" style={{ margin: '0 auto', }}>
{/* NOTE: This div */}
<div>
<div styleName="form-field">
<div>Broker Link</div>
<input
type="text"
name="brokerLink"
value={brokerLink}
placeholder=""
onChange={e => setBrokerLink(e.target.value)}
/>
</div>
</div>
</div>
... [Other code] ...
);
... [Other code] ...
Was very strange. Without this extra div, it seems react initially rendered the input element with no value attribute but with an empty style attribute for some reason. You could see that by looking at the html. And this led to the console warning..
What was even weirder was that adding a default value that is not an empty string or doing something like value={brokerLink || ''} would result in the exact same problem..
Another weird thing was I had 30 other elements that were almost exactly the same but did not cause this problem. Only difference was this brokerLink one did not have that outer div..
And moving it to other parts of the code without changing anything removed the warning for some reason..
Probably close to impossible to replicate without my exact code. If this is not a bug in react or something, I don't know what is.
The problem occurs even if you set undefined to the value at a previous rendering that happened even before initializing things properly.
The issue went by replacing
value={value}
with
value={(value==undefined?null:value)}
For me, this was the mistake:
<input onChange={onClickUpdateAnswer} value={answer.text}>
{answer.text}
</input>
As you see, I have passes string into the body of the Input tag,
Fix:
<input onChange={onClickUpdateAnswer} value={answer.text}></input>