Reputation: 2513
I have been reading some threads on SO but I could not figure out how to solve this issue or why it's happening. Can someone explain it like I'm 5?
Warning: A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component
I am developing a lesson creator, and the user must be able to open an existing lesson, hence the input fields must be programmatically filled with the content of the existing lesson.
My constructor:
constructor(props) {
super(props);
this.state = {
lessonID: -1,
sectionsArray: [],
title: 'No title',
type: 'r',
language: 'gb',
book: 'booka',
level: '1',
loading: false,
saved: true,
messageBox: '',
lessonOpenModal: false,
}
this._state = this.state;
this.updateSectionsFromChild = this.updateSectionsFromChild.bind(this);
this.sectionAdd = this.sectionAdd.bind(this);
this.sectionRemove = this.sectionRemove.bind(this);
this.menuInput = this.menuInput.bind(this);
this.menuDropDown = this.menuDropDown.bind(this);
this.lessonCreate = this.lessonCreate.bind(this);
this.lessonSave = this.lessonSave.bind(this);
this.lessonDelete = this.lessonDelete.bind(this);
this.lessonOpen = this.lessonOpen.bind(this);
this.sections = [];
}
This are the functions that update the controlled components:
menuDropDown(event, data) {
this.setState({
[data.name]: data.value,
saved: false,
});
console.log(data.name);
console.log(data.value);
}
menuInput(event) {
this.setState({
[event.target.name]: event.target.value,
saved: false,
});
}
And then this is the part of code that retrieves the lesson and tries to update the state:
async openLesson(lessonID) {
await ARLessonOpen(lessonID).then((result) => {
this.setState(this._state);
this.setState({
id: result.lesson.id,
language: result.lesson.language,
book: result.lesson.book, // this is a drop down, and it's not causing errors
type: result.lesson.type, // this is a drop down, and it's not causing errors
level: result.lesson.level, // this is a drop down, and it's not causing errors
title: result.lesson.title, // this is an input, and IT'S THE ISSUE
sectionsArray: result.sections.map((section, i) => ({
key: i,
id: i,
title: section.title,
duration: section.duration,
content: section.content,
}))
})
}).catch(function(error) {
console.log(error);
});
}
The only field that is not working is the 'title' and I can't understand why. How can I update the input value programmatically?
JSX:
renderSections = () => {
if (this.state.sectionsArray.length > 0) {
return this.state.sectionsArray.map((section, i) =>
<LessonSection
key={section.id}
id={section.id}
title={section.title}
duration={section.duration}
content={section.content}
sectionRemove={this.sectionRemove}
sectionAdd={this.sectionAdd}
updateSectionsFromChild={this.updateSectionsFromChild}
/>
)
} else {
return (
<div style={{color: 'black'}}>
<Button
size='mini'
icon='plus square outline'
onClick={this.sectionAdd} />
Add a section to start creating your lesson.
</div>
)
}
}
render() {
return (
<div className='Lesson-editor'>
{this.state.messageBox}
<div style={{display: 'none'}}>
<DefaultLoader
active={this.state.loading}
message={this.state.message}
/>
</div>
<div className="Lesson-editor-menu Small-font">
<div className="Menu-buttons">
<Button
size='mini'
icon='plus square outline'
onClick={this.sectionAdd} />
<Button
size='mini'
icon='file outline'
onClick={this.lessonCreate} />
<DialoglessonOpen
open={this.state.lessonOpenModal}
actionOnLessonSelected={(lessonID) => this.openLesson(lessonID)}
onCancel={() => this.setState({lessonOpenModal: false})} />
<Button size='mini' icon='open folder outline' text='Open lesson' description='ctrl + o' onClick={this.lessonOpen} />
<Button
size='mini'
icon='save outline'
onClick={this.lessonSave} />
<Button
size='mini'
icon='delete'
onClick={this.lessonDelete} />
<Button
size='mini'
icon='delete'
color='red'
onClick={ARClearTables} />
</div>
<Input
className='title'
fluid
placeholder='Lesson title'
value={this.state.title}
name='title'
onChange={this.menuInput}
/>
<div>
<Dropdown
fluid
compact
placeholder='Language'
search
selection
options={lessonLanguages}
//defaultValue='gb'
value={this.state.language}
name='language'
onChange={this.menuDropDown}
/>
<Dropdown
fluid
compact
placeholder='Book'
search
selection
options={lessonBooks}
//defaultValue='booka'
value={this.state.book}
name='book'
onChange={this.menuDropDown}
/>
<Dropdown
fluid
compact
placeholder='Lesson type'
search
selection
options={lessonTypes}
defaultValue='r'
name='type'
onChange={this.menuDropDown}
/>
<Dropdown
fluid
compact
placeholder='Lesson level'
search
selection
options={lessonLevels}
defaultValue='1'
name='level'
onChange={this.menuDropDown}
/>
</div>
</div>
<div className='Sections'>
{ this.renderSections() }
</div>
</div>
);
}
}
Upvotes: 2
Views: 638
Reputation: 2513
I figured it out: the problem is that there was an error in my code. I was assigning a null
value to the input field value in state
.
async openLesson(lessonID) {
await ARLessonOpen(lessonID).then((result) => {
this.setState(this._state);
this.setState({
/* HERE: I try to access result.lesson but it's null! I should
use result.lesson[0]. So the problem is that I was
assigning a null value to the input field resulting in the error */
id: result.lesson.id,
language: result.lesson.language,
book: result.lesson.book,
type: result.lesson.type,
level: result.lesson.level,
title: result.lesson.title,
sectionsArray: result.sections.map((section, i) => ({
key: i,
id: i,
title: section.title,
duration: section.duration,
content: section.content,
}))
})
}).catch(function(error) {
console.log(error);
});
}
Upvotes: 0
Reputation: 24650
The initial value of input forms fields cannot be undefined or null, if you want to control it later. It should be an empty string. If you provide an undefined or null it's uncontrolled component.
In you code, React doesn't see any value to the input fields, so React believe it is a un-controlled component on the first mount. Later, when you add a value to the component React warning you that you can't give a value (controlled component) after you didn't provided a value (uncontrolled component)
Upvotes: 3