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>
);
});
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 .
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
}
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>
);
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});
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.