简体   繁体   中英

React: How to set state of object property?

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 .

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>
 );

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM