I've asked for more information about the error here: Should I ignore React warning: Input elements should not switch from uncontrolled to controlled?. But I'm still stuck..
So I have a case where I want to switch a time input field from holding it's own value to taking over a value from another time input field. I'm doing this using a simple 'locked' / 'unlocked' button. Clicking on that button allows me to choose whether the input field is locked (takes over value from others) or unlocked (keeps its own value).
I'm doing this inside a loop because each time field will be repeated 7 times (for each day).
Problem
However, each time I switch from regular time input field to a time input field with actions behind it, React's giving me the following error:
A component is changing an uncontrolled input of type time 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
Explaining the code below
The part between comments goes as follow: If the locked state of the current day input field is 'true', then the input must take a value from a state and execute a onchange handler. If it is false, it shouldn't do anything and react as a basic input field.
What I have
export class OpeningHoursTable extends Component{
constructor(props) {
super(props);
this.state = {
sharedInputMo : '',
empty: undefined
};
}
render() {
const Days = [{id: 1, day: 'Monday'},{id: 2, day: 'Tuesday'},{id: 3, day: 'Wednesday'},{id: 4, day: 'Thursday'},{id: 5, day: 'Friday'},{id: 6, day: 'Saturday'},{id: 7, day: 'Sunday'}];
const Table = Days.map(({id, day}) => (
<div key={id} className='ohTableRow'>
<div className='ohTableMorning'>
<div className='ohTableContentBlock'>{day}</div>
<div className='ohTableContentBlock'>
// THIS IS THE PART THAT GIVES ME THE ERROR
<input type='time'
value={this.state['lock' + day + '_mo'] === true ? this.state.sharedInputMo || this.state.empty: this.state.empty}
onChange={this.state['lock' + day + '_mo'] === true ? thisInput => this.setState({sharedInputMo : thisInput.target.value }) : null }
/>
// THIS IS THE PART THAT GIVES ME THE ERROR
</div>
<div className='ohTableLockState' onClick={this.state['lock' + day + '_mo'] === true ? () => this.setState({ ['lock' + day + '_mo'] : false }) : () => this.setState({ ['lock' + day + '_mo'] : true }) }>
{this.state['lock' + day + '_mo'] === true ?
<Icon name='Locked' />
:
<Icon name='Unlocked' />
}
</div>
</div>
</div>
));
return (
<div className='ohTable'>
{Table}
</div>
);
}
}
So after giving it a night's rest I solved my own problem.
This is for everyone who needs to switch between a controlled and uncontrolled object and has the same error.
Use a Switch statement to switch between a controlled and uncontrolled object. This way, React will see them both as a separate and different object; thus in front end it will seem as if you have the same object.
The root of your problem and the error is that you provide
undefined
as a value to input and do not assign anyonChange
event initially.More specifically you pass
this.state.empty
as a value, which is assigned anundefined
in the state andnull
asonChange
handler.When react gets an
undefined
as a value of input and no handler, it assumes that this input is uncontrolled and all the changes will be handled by the DOM and react itself should not do anything with it.But, later down the road, based on user action, your input gets another value
this.state.sharedInputMo
andonChange
handler that turns it into controlled input. This change causes the confusion of react and gives you this warning.