简体   繁体   中英

Update state of array nested inside object (ReactJS)

I have an object in state ("car") with multiple keys, one of which is an array ("features"). There's a couple things I'm trying to do with it.

  1. I want to push another string (another feature) onto the "features" array every time I click the "Add Feature" button.
  2. I want to be able to update the state of each string/feature in the "features" array when I type in the respective input.

I've researched this online quite a bit and haven't found anything (maybe because this isn't possible). Either way, here's my code:

class Car extends React.Component {

  state = {
    car: {make: 'Toyota', model: 'Camry', features: []},
  }


  handleChange = (e, index) => {
    const value = e.target.value
    let features = this.state.car.features.slice() // create mutable copy of array
    features = features[index].concat(value)
    this.setState({...this.state.car, features: features})
  }

  handleAddFeature = () => {
    let features = this.state.car.features.slice()
    features.push('')
    this.setState({...this.state.car, features: features})
  }

  render() {
    return (
      {
        this.state.car.features.map((f, index) => { return <input key={index} onChange={e => this.handleChange(e, index)}>{feature}</input>
      }
      <button onClick={this.handleAddFeature}>Add Feature</button>
    )
  }
}

Okay, I got this working in a very similar way to @Snkendall's answer. My handleChange function is listed below:

handleChange = (e, index,) => {
  let array = this.state.car.features.slice() // create mutable copy of the array
  array[index] = e.target.value // set the value of the feature at the index in question to e.target.value
  const newObj = { ...this.state.car, features: array } // create a new object by spreading in the this.state.car and overriding features with our new array 
  this.setState({ car: newObj }) // set this.state.car to our new object
}

The difference between this solution and @Snkendall's is the defining of a newObj variable. That turns out to the be the only way to update an individual key nested inside a state object.

Ad.1

  handleAddFeature = () => {
    const car = this.state.car
    car.features.push('Your feature')
    this.setState({ car })
  }

Just create copy and push to car features new value.

Ad. 2 Create component called eg Feature. He will have own state where you modify string and through props you can extract data to your "car".

There are a few things that could be causing you problems... if your component has a state, you should use a constructor, and bind your 'this' references inside it to prevent 'this' from referencing the global. You just wrap your state like this:

class Car extends React.Component {
   constructor() {
     super()
     this.state = {
      car: {make: 'Toyota', model: 'Camry', features: []},
    }
    this.handleChange = this.handleChange.bind(this)
    this.handleAddFeature = this.handleAddFeature.bind(this)
  }

This is a really great article for thinking about 'this': http://2ality.com/2017/12/alternate-this.html

Another area that might cause you problems is features = features[index].concat(value) ... because you're concatting the input tag's value onto the current string on state over and over again with every change (keystroke). You can just reset the value of the element at that index in the array like this:

handleChange = (e, index) => {
  const value = e.target.value
  let features = this.state.car.features.slice()
  features[index] = value
  this.setState({...this.state.car, features})
}

and that way, each keystroke just resets the value on state to reflect the change created in the input. You actually wouldn't need to use the handleAddFeature at all, since the state is already updated with handleChange.

I'm changing features:features to just features because ES6 destructuring has this fun thing where if a key and it's value is the same, you only need to reference it once, and it figures it out. It's just a cool way to keep your code even DRYer.

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