0

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>
    )
  }
}
1
  • Can you give an example of "I want to be able to update the state of each string/feature in the "features" array when I type in the respective input?" Commented Dec 15, 2017 at 0:14

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

Okay, thanks for your help here and sorry for the delayed reply. I ended up doing something very similar to your suggestion here which I outlined in an answer below since I needed more room.
0

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 e.g. Feature. He will have own state where you modify string and through props you can extract data to your "car".

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.