105

I have an array in state, let's say this.state.arr. I want to add something to this state property, and then change some more properties.

Option 1

onChange(event){
    this.state.arr.push('newvalue');
    ...
    this.setState({some:'val',arr:this.state.arr})
}

Option 2

onChange(event){
    var newArr = this.state.arr;
    ...
    newArr.push('newvalue');
    ...
    this.setState({some:'val',arr:newArr})
}

So... I know this.state is supposed to be treated immutable, but is it ok to use it like in option 1 where I still set the state from it, or do I need to go with something like option 2, and thus always first making a copy in memory?

2

13 Answers 13

121

For now, this is the best way.

this.setState(previousState => ({
    myArray: [...previousState.myArray, 'new value']
}));
Sign up to request clarification or add additional context in comments.

5 Comments

This worked for me. It also works for prepending into the array: this.setState((prevState) => ({ myArray: [values, ...prevState.myArray], }));
If I want 'new value' to be a parameter that I passed in. Do I simply do onChange = (id) => { this.setState(prevState => ({ tags: [...prevState.tags, id] })); }
How to add new value to a specific position in the array?
@PubuduJayasanka setFoo(prevState => [...prevState.slice(0, pos), newItem, ...prevState.slice(pos)]) with hooks, but you can use a similar spread pattern on a specific key in a class component.
how to only add unique value?
67

Both of the options you provided are the same. Both of them will still point to the same object in memory and have the same array values. You should treat the state object as immutable as you said, however you need to re-create the array so its pointing to a new object, set the new item, then reset the state. Example:

onChange(event){
    var newArray = this.state.arr.slice();    
    newArray.push("new value");   
    this.setState({arr:newArray})
}

2 Comments

Slice will create a new shallow copy of the array, making it immutable.
@Butters FWIW, that doesn't make it immutable since objects in the array can still be mutated. Here it's strings so it's not an issue, but in the general case, slice still allows in situ updates, bypassing the normal state checking.
61

Another simple way using concat:

this.setState({
  arr: this.state.arr.concat('new value')
})

3 Comments

I find this both the most expressive and least verbose method.
Changed this to the accepted answer because not only is it the last verbose, it's also the fastest solution according to jsperf
This could cause the state to get out of sync, if the setState is not called in the expected order. Better to use the setState(prevState, ...).
50

If you are using ES6 syntax you can use the spread operator to add new items to an existing array as a one liner.

// Append an array
const newArr = [1,2,3,4]
this.setState(prevState => ({
  arr: [...prevState.arr, ...newArr]
}));

// Append a single item
this.setState(prevState => ({
  arr: [...prevState.arr, 'new item']
}));

Comments

40

Short way with useState hook:

const [value, setValue] = useState([])
setValue([...value, newvalue])

Comments

9

the best away now.

this.setState({ myArr: [...this.state.myArr, new_value] })

1 Comment

I am wondering how can I insert new value to the middle of the array instead of adding it to the end?
9

For functional components with hooks

const [searches, setSearches] = useState([]);

// Using .concat(), no wrapper function (not recommended)
setSearches(searches.concat(query));

// Using .concat(), wrapper function (recommended)
setSearches(searches => searches.concat(query));

// Spread operator, no wrapper function (not recommended)
setSearches([...searches, query]);

// Spread operator, wrapper function (recommended)
setSearches(searches => [...searches, query]);

source: https://medium.com/javascript-in-plain-english/how-to-add-to-an-array-in-react-state-3d08ddb2e1dc

1 Comment

"setSearches(searches => searches.concat(query)); " is by far the best solution in 2021, thank you king.
5

onChange() {
     const { arr } = this.state;
     let tempArr = [...arr];
     tempArr.push('newvalue');
     this.setState({
       arr: tempArr
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Comments

5

React hook - useState (2022)

const [array, setArray] = useState([]);

const handleChange = (newValue) => {
  setArray((array) => [...array, newValue]);
};

Comments

2
handleValueChange = (value) => {
      let myArr= [...this.state.myArr]
      myArr.push(value)
      this.setState({
         myArr
     })

This might do the work.

Comments

2
const [array, setArray] = useState([]);

const handleChange = (newValue) => {
  setArray((prevState) => [...prevState, newValue]);
};

If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.

setState doc.

Comments

0

If you want to keep adding a new object to the array i've been using:

_methodName = (para1, para2) => {
  this.setState({
    arr: this.state.arr.concat({para1, para2})
  })
}

Comments

0

This might not directly answer your question but for the sake of those that come with states like the below

 state = {
    currentstate:[
     {
    id: 1 ,
    firstname: 'zinani',
    sex: 'male'

     }
    ]

 }

Solution

const new_value = {
    id: 2 ,
    firstname: 'san',
    sex: 'male'

     }

Replace the current state with the new value

 this.setState({ currentState: [...this.state.currentState, new_array] })

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.