Pushing objects from an observable overwrites previous data in an array

Introduction

I came across a very strange behaviour with Javascript arrays and Observables when working on an Angular app.

I was trying to create an onClick() method that sends an HTTP requests and its response value is pushed to an array.

The code was somehow like:


@Component()
export class AppComponent {
subj: Subject = new Subject();
items: Array<Item> = [];

onClick(id){
this.http.get('https://cyphercodes.github.io/getItems?id='+id).subscribe(res => {
this.items.push(res); // assuming that res is a Item object
});
}
}

Strangely, every time an object is pushed into the array items, it is overwriting all the previous values of the array and then pushing a new object!

After some research, I came across this Stack Overflow where James explains the cause perfectly:

https://stackoverflow.com/questions/19054997/push-is-overwriting-previous-data-in-array

Objects are passed by reference and in the above example it seems that Angular’s HTTP request sends the same object with each request without creating a new object resulting in the same object with the same reference being pushed into the array every time but with different values. This causes Array.push() to overwrite the members of the array.

Issue Replication

In the JSFiddle below, an attempt was made to replicate the issue:

We are mimicking a service that sends the same object with different values every 1 second to a Subject Observable that we created.

Then, we subscribe to that service and push every result object to an array.

If you click on the Result tab of this fiddle, you should notice that all the array elements are being overwritten with every push.

Fix

One way of fixing this is to push a copy of the object to the array in the subscribe function instead of pushing the object itself.

This can be done by using Object.assign() as shown below:

let arr = [];
subj.subscribe(v => {
arr.push(Object.assign({},v));
console.log(arr);
});

You can find the fix applied on the JSFiddle below:

If you check the Result tab you should realize that the array objects are no longer overwritten with every push.