2

I want to update array value using index, is below code ok?

handleChange = index => e => {
  const { rocket } = this.state // ['tesla', 'apple', 'google']
  rocket[index] = e.target.value
  this.setState({ rocket })
}

my jsx

<div>{rocket.map((val,i) => <input type="text" onChange={handleChange(i)} value={val} />)}</div>

I know it worked, but just to be sure it's ok to mutate the state like that.

3 Answers 3

5

It's not okay to mutate state this way.

The following line mutates the array in the current state in a way that can lead to bugs in your program particularly with components down the Component tree using that state.
This is because the state is still the same array.

rocket[index] = e.target.value
//console.log(this.state.rocket) and you see that state is updated in place

Always treat state as immutable

You can remedy this by writing.

const newRocket = [
  ...rocket.slice(0, index),
  e.target.value, 
  ...rocket.slice(index + 1)
]

This way a new array is created and components in the Component tree can be updated when React does a reconciliation.

Note that
The only way to mutate state should be through calls to Component.setState.

Now that you have a new array, you can update the component state like so:

this.setState({ rocket: newRocket })
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, mapping over the array items creates a new array. However, it means writing code like this: this.setState({rocket: rocket.map((val, idx) => i === idx ? e.target.value : val)})
0

Instead of changing existing value, you could use Array.prototype.splice().

The splice() method changes the contents of an array by removing existing elements and/or adding new elements.

var arr= ['A','B','E','D'];
arr.splice(2,1,'C')
console.log(arr)//The result will be  ['A','B','C','D'];
.as-console-wrapper {max-height: 100% !important;top: 0;}

Stackblitz demo


CODE SNIPPET

class App extends Component {
  constructor() {
    super();
    this.state = {
      name: 'Demo using Array.prototype.slice()',
      rocket: ['tesla', 'apple', 'google'],
      link: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice'
    };
  }

  handleChange(index, e) {
    const { rocket } = this.state;
    rocket.splice(index, 1, e.target.value)
    this.setState({ rocket: [...rocket] }, () => {
      //call back function of set state you could check here updated state
      console.log(this.state.rocket)
    });
  }

  render() {
    return (
      <div>
        <b><a target="_blank" href={this.state.link}>{this.state.name}</a></b>
        {
          this.state.rocket.map((val, i) =>
            <p key={i}>
              <input type="text" onChange={(e) => { this.handleChange(i, e) }} value={val} />
            </p>)
        }</div>
    );
  }
}

render(<App />, document.getElementById('root'));

2 Comments

using map is doable because I can check the index too right?
The map() method creates a new array with the results of calling a provided function on every element in the calling array. that's why is doable.
0

you can try on this



  const onChangeProduct = (e, index) => {
    console.log("Value price " + e);
    const updateItem =  [...imports];
    updateItem[index]["product"] = e;
    setImports(updateItem);

  }



 <Select
         className="w-40"
            showSearch
            placeholder="Select product"
            optionFilterProp="children"      
            value={element.product}
            onChange= {(e) => onChangeProduct(e, index)}
            options={
                items
            }
          />





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.