98

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?

Jasperan
  • 2,154
  • 1
  • 16
  • 40
Flion
  • 10,468
  • 13
  • 48
  • 68
  • 4
    see this http://stackoverflow.com/questions/26253351/correct-modification-of-state-arrays-in-reactjs#comment41184522_26253351 and this http://stackoverflow.com/questions/23966438/what-is-the-preferred-way-to-mutate-a-react-state – nilgun Oct 22 '14 at 11:02
  • A third option could be the best : use immutability helper provided by React : https://facebook.github.io/react/docs/update.html – rebe100x Jan 27 '16 at 12:33

13 Answers13

116

For now, this is the best way.

this.setState(previousState => ({
    myArray: [...previousState.myArray, 'new value']
}));
Kutsan Kaplan
  • 1,755
  • 3
  • 14
  • 22
  • 1
    This worked for me. It also works for prepending into the array: `this.setState((prevState) => ({ myArray: [values, ...prevState.myArray], }));` – Gus Oct 23 '18 at 17:35
  • 2
    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] })); } ``` – Liu Hantao May 13 '19 at 10:05
  • How to add new value to a specific position in the array? – Pubudu Jayasanka Jul 30 '20 at 02:29
  • 1
    @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. – ggorlen Nov 18 '21 at 00:45
  • how to only add unique value? – huykon225 Jun 29 '22 at 14:35
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})
}
Butters
  • 850
  • 7
  • 7
  • 2
    Slice will create a new shallow copy of the array, making it immutable. – Butters Sep 14 '16 at 20:52
  • @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. – Dave Newton Oct 07 '18 at 20:18
59

Another simple way using concat:

this.setState({
  arr: this.state.arr.concat('new value')
})
TiagoLr
  • 2,782
  • 22
  • 16
  • 7
    I find this both the most expressive and least verbose method. – Jesper We Dec 12 '16 at 19:29
  • 3
    Changed this to the accepted answer because not only is it the last verbose, it's also the fastest solution according to jsperf – Flion Jul 21 '19 at 11:29
  • 1
    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, ...). – peter.swallow Apr 26 '21 at 23:01
49

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']
}));
JamieD
  • 2,707
  • 2
  • 20
  • 19
33

Short way with useState hook:

const [value, setValue] = useState([])
setValue([...value, newvalue])
Gass
  • 7,536
  • 3
  • 37
  • 41
Tony
  • 493
  • 4
  • 7
8

the best away now.

this.setState({ myArr: [...this.state.myArr, new_value] })
Mas dem
  • 97
  • 1
  • 1
6

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

Codex
  • 1,153
  • 1
  • 20
  • 31
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>
SAMAR
  • 59
  • 1
  • 8
4

React hook - useState (2022)

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

const handleChange = (newValue) => {
  setArray((array) => [...array, newValue]);
};
2
handleValueChange = (value) => {
      let myArr= [...this.state.myArr]
      myArr.push(value)
      this.setState({
         myArr
     })

This might do the work.

Harshit Singhai
  • 1,150
  • 11
  • 7
1
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.

Ivan K.
  • 748
  • 7
  • 11
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})
  })
}
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] })
Excellent Lawrence
  • 946
  • 11
  • 11