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 ponent 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>
);
});
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 ponent 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>
);
});
Share
Improve this question
asked Oct 8, 2018 at 22:18
John MuttonJohn Mutton
531 gold badge1 silver badge8 bronze badges
2 Answers
Reset to default 1SetState 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 />
ponent (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 ment in code */
onChange={this.handleDesc}
/>
) : null }
</div>
);