278

I seem to be having issues pushing data into a state array. I am trying to achieve it this way:

this.setState({ myArray: this.state.myArray.push('new value') })

But I believe this is incorrect way and causes issues with mutability?

Ilja
  • 44,142
  • 92
  • 275
  • 498
  • 2
    `Array.push` returns the new `length` of the array instead of returning new array which causes issue – Bharat Jan 13 '22 at 07:36

15 Answers15

305

Using es6 it can be done like this:

this.setState({ myArray: [...this.state.myArray, 'new value'] }) //simple value
this.setState({ myArray: [...this.state.myArray, ...[1,2,3] ] }) //another array

Spread syntax

Aliaksandr Sushkevich
  • 11,550
  • 7
  • 37
  • 44
  • 1
    I did the same, there are two cases myArray can have values and it won't. so in that, if it has already values, it works perfectly fine. But in no data..it does not update state with 'new value'. Any soln ? – Krina Soni Mar 08 '18 at 09:17
  • It should work with any arrays, doesn't matter if it has values or not it will be destructured anyway. Maybe there is something wrong in other place. Could you please show an example of your code? – Aliaksandr Sushkevich Mar 08 '18 at 18:52
  • hi Please refer my comment under @Tamas answer. It was just a sample in console. I tried myArray: [...this.state.myArray, 'new value'] to update my state array.But it concats only the last value.Could you plz tell me the solution? – Johncy Aug 31 '18 at 13:10
  • @Johncy I’m not sure if your issue is related to this question, try to ask a separate question and describe the expected behavior and I’ll try to help you. – Aliaksandr Sushkevich Aug 31 '18 at 17:09
  • 13
    Per the React docs: "Because `this.props` and `this.state` may be updated asynchronously, you should not rely on their values for calculating the next state." In the case of modifying an array, since the array already exists as a property of `this.state` and you need to reference its value to set a new value, you should use the form of `setState()` that accepts a function with the previous state as an argument. Example: `this.setState(prevState => ({ myArray: [...this.state.myArray, 'new value'] }));` See: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous – Bungle Feb 09 '19 at 02:25
  • how can also do this for an array response of an API fetch? – henrie Jul 31 '19 at 01:00
  • Are there any advantages/disadvantages for using spread syntax over concat() – bitcasual Jul 14 '21 at 08:38
  • @Bungle Surely, you would need to refer to `prevState`, instead of `this.state.myArray` in your call, otherwise there's no point to using the form you are suggesting: `this.setState(prevState => ({ myArray: [...prevState.myArray, 'new value'] }));` – Maximvs Jun 25 '22 at 10:29
215

Array push returns length

this.state.myArray.push('new value') returns the length of the extended array, instead of the array itself.Array.prototype.push().

I guess you expect the returned value to be the array.

Immutability

It seems it's rather the behaviour of React:

NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.React.Component.

I guess, you would do it like this (not familiar with React):

var joined = this.state.myArray.concat('new value');
this.setState({ myArray: joined })
Edgar
  • 6,022
  • 8
  • 33
  • 66
Márton Tamás
  • 2,759
  • 1
  • 15
  • 19
  • 1
    When I do `console.log(this.state.myArray)` it's always one behind. Any idea why? – Si8 Jul 20 '19 at 17:53
  • @Si8 Well, I don't use React too much unfortunately. But the docs say: *`setState()` enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state.* So I guess it's just not updated at that moment right after setting it. Could You please post a code example, where we can see which point You are setting and logging it, please? – Márton Tamás Jul 22 '19 at 10:36
  • Thanks for the response. It's async so it won't show you the changes right away. However setState does have a callback which did display the correct value. Thanks again. – Si8 Jul 22 '19 at 13:58
  • 1
    https://www.w3schools.com/jsref/jsref_concat_array.asp concat concatenates two arrays (not array and string) , `.concat('new value');` should be `.concat(['new value']);` – Manohar Reddy Poreddy Oct 19 '19 at 15:22
  • 1
    @ManoharReddyPoreddy Non-array values are perfectly valid for the `concat()` method. See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat (_Arrays and/or values to concatenate into a new array._) – Márton Tamás Oct 20 '19 at 19:37
  • If you simply do `this.state.myArray.push('new values')` will that trigger a re-render like how `setState` would? – Joyce Lee Dec 02 '20 at 22:17
  • @JoyceLee I'm not experienced with ReactJS, so I can't tell for sure, but frontend frameworks usually recognize modifications originating from the variable's method (`push` in this case). Frameworks tend to fail when you re-assign value to the variable. – Márton Tamás Dec 02 '20 at 22:26
206

Functional Components & React Hooks

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

Push value at the end:

setArray(oldArray => [...oldArray,newValue] );

Push value at the start:

setArray(oldArray => [newValue,...oldArray] );
Panagiss
  • 3,154
  • 2
  • 20
  • 34
  • 1
    what do I do if I have an array of objects to add to an existing array? – Souvik Ray Jun 24 '21 at 03:28
  • 1
    Short modification - the snippet for push value at the beginning shall be: `setArray(oldArray => [newValue,...oldArray] );` – Ndrslmpk Nov 04 '21 at 11:20
  • @SouvikRay you can use Array.concat to merge two arrays and return new array. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat – Bharat Jan 13 '22 at 07:38
165

Never recommended to mutate the state directly.

The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions:

Push string to end of the array

this.setState(prevState => ({
  myArray: [...prevState.myArray, "new value"]
}))

Push string to beginning of the array

this.setState(prevState => ({
  myArray: ["new value", ...prevState.myArray]
}))

Push object to end of the array

this.setState(prevState => ({
  myArray: [...prevState.myArray, {"name": "object"}]
}))

Push object to beginning of the array

this.setState(prevState => ({
  myArray: [ {"name": "object"}, ...prevState.myArray]
}))
Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162
  • 1
    I used this answer. It also works for prepending into the array: `this.setState((prevState) => ({ myArray: [values, ...prevState.myArray], }));` – Gus Oct 23 '18 at 17:34
  • 2
    this is a much better approach than the accepted answer and does it the way React documentation recommends. – Ian J Miller Jun 07 '19 at 16:03
  • 2
    Definitely +1ing this because the other answers don't follow the latest guidelines of using callbacks if mutating state with itself. – Matt Fletcher Aug 29 '19 at 13:30
  • how to add another array objects in state array? – Chandni Mar 03 '20 at 06:55
  • This creates a new array that becomes myArray: updatedContents rather than just leaving it as an array of the updated contents. If you're mapping the original array, this will cause an error due to the new structure. Is that intentional? – Dr J Sep 04 '21 at 19:36
  • To prevent what I noted above: `myArray((prevState) => [...prevState, ...newContents]);` – Dr J Sep 04 '21 at 19:39
26

You should not be operating the state at all. At least, not directly. If you want to update your array, you'll want to do something like this.

var newStateArray = this.state.myArray.slice();
newStateArray.push('new value');
this.setState(myArray: newStateArray);

Working on the state object directly is not desirable. You can also take a look at React's immutability helpers.

https://facebook.github.io/react/docs/update.html

Christoph
  • 978
  • 7
  • 17
  • 5
    I believe this answer to be the correct one although I would have liked to know why we can't operate on state, i.e. why it is not desirable. After a little digging I found the following [React tutorial - Why Immutability is Important](https://reactjs.org/tutorial/tutorial.html#why-immutability-is-important), which helped to fill in the missing info and the tutorial also uses `.slice()` to create a new array and preserve immutability. Thanks for the help. – BebopSong Apr 10 '18 at 21:57
23

Here you can not push the object to a state array like this. You can push like your way in normal array. Here you have to set the state,

this.setState({ 
     myArray: [...this.state.myArray, 'new value'] 
})
Sachin Muthumala
  • 775
  • 1
  • 9
  • 17
17

You can use .concat method to create copy of your array with new data:

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

But beware of special behaviour of .concat method when passing arrays - [1, 2].concat(['foo', 3], 'bar') will result in [1, 2, 'foo', 3, 'bar'].

Ginden
  • 5,149
  • 34
  • 68
15

Using react hooks, you can do following way

const [countryList, setCountries] = useState([]);


setCountries((countryList) => [
        ...countryList,
        "India",
      ]);
Rajesh N
  • 6,198
  • 2
  • 47
  • 58
4

This Code work for me :

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
  this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
})
  • 3
    Welcome to Stack Overflow! Please don't answer just with source code. Try to provide a nice description about how your solution works. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). Thanks – sɐunıɔןɐqɐp Sep 24 '18 at 07:27
  • I tried this but to no avail. Here's my code ```fetch(`api.openweathermap.org/data/2.5/forecast?q=${this.searchBox.value + KEY} `) .then( response => response.json() ) .then( data => { this.setState({ reports: this.state.reports.push.apply(this.state.reports, data.list)}); }); ``` – henrie Jul 31 '19 at 01:07
  • and i firstly initialized state as an empty array i.e ```this.state = { reports=[] }```...pls i'll like to know what i'm doing wrong – henrie Jul 31 '19 at 01:11
  • @Hamid Hosseinpour – henrie Jul 31 '19 at 23:54
2

React-Native

if u also want ur UI (ie. ur flatList) to be up to date, use PrevState: in the example below if user clicks on the button , it is going to add a new object to the list( both in the model and UI)

data: ['shopping','reading'] // declared in constructor
onPress={() => {this.setState((prevState, props) => {
return {data: [new obj].concat(prevState.data) };
})}}. 
Community
  • 1
  • 1
Mahgolsadat Fathi
  • 3,107
  • 4
  • 16
  • 34
2

In the following way we can check and update the objects

this.setState(prevState => ({
    Chart: this.state.Chart.length !== 0 ? [...prevState.Chart,data[data.length - 1]] : data
}));
KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133
2
  setState([...prevState, {
    label: newState.name,
    value: newState.id
  }]);

Was working with the dropdowns and wanted to implement this scenario there, i found this simple solution for dropdown with multiple values.

1

If you use:

const[myArr, setMyArr] = useState([]);

for add:

setMyArr([...myArr, value]);

and for remove:

let index = myArr.indexOf(value);
if(index !== -1)
    setPatch([...myArr.slice(0, index), ...myArr.slice(index, myArr.length-1)]);
0

you are breaking React principles, you should clone the old state then merge it with the new data, you shouldn't manipulate your state directly, your code should go like this

fetch('http://localhost:8080').then(response => response.json()).then(json ={this.setState({mystate[...this.state.mystate, json]}) })

-1

I guess this is a little bit late for an answer but for those new to react

You can use this tiny package called immer

see this example: https://immerjs.github.io/immer/produce

Nevin
  • 365
  • 5
  • 18