Immutability helps reduce hard to catch bugs sometimes, but that may not always be required or be the ideal case.
What is Immutability ?
Not changing the value refers to immutability. In Javascript, array and objects are mutable.
Examples of immutability:
let arr = ['John', 'Alice'];
// Adding element is mutating
arr.push('Oliver');
// Updating data is mutating
arr[1] = 'Lily'
Lot of other functions can mutate this array. More here
- copyWithin
- fill
- pop
- push
- reverse
- shift
- sort
- splice
- unshift
Also, note that defining an array or object as a const does not guarantee immutability.
For example:
const obj = {
name: 'foo'
}
// update name
obj.name = 'bar';
console.log(obj);
const arr = ['foo'];
// push new item
arr.push('bar');
console.log(arr);
Now to answer your question. when can spread operator mutate and when does is not ?
If we have this array, whenever any data in this array mutates, then it loses immutability.
let arr = ['Obito', 'Sasuke'];
let sameReferenceArray = arr;
console.log(arr === sameReferenceArray);
// here the original array did not mutate
let newArray = [...arr, 'Madara'];
console.log(arr === sameReferenceArray);
// but here it did mutate as the new array is assigned back to arr
arr = [...arr, 'Madara'];
console.log(arr === sameReferenceArray);
There are more examples of what methods mutate and what don't and you'll learn them over time.
Edit
How Mutation can have negative impact ?
// simple example demostrating negative impact of mutataion
const obj = { name: "foo" };
const arr = [obj, obj]
console.log(arr);
// Mutating first element
arr[0].name = "bar";
console.log("Contents after mutation", arr);
As you can see, we ended up updating both objects even though we update the one at index 0.
Now I'll spread and mutate:
const obj = { name: "foo" };
const arr = [{...obj}, {...obj}]
console.log(arr);
// Mutating first element
arr[0].name = "bar";
console.log("Contents after mutation", arr);
Only the intended element is mutated now.
There is a lot more to this. In general, Immutability shouldn't be a very big concern if you've started out to learn. But knowing it certainly helps.
For example in React state should not be mutated (reason)
And at last, regarding how to update `aggregatedData'.
const aggregator = () => {
let aggregateData = [];
let hasNextPage;
let page = 1;
do {
const { data, nextPage } = pagedData[page - 1];
aggregateData = [...aggregateData, ...data];
page = nextPage;
hasNextPage = nextPage;
} while (hasNextPage);
return aggregateData;
}
Since you're prepending data to an array you'll end up mutating it anyway. You can do spread or push or whatever. This all is fine since the array is locally created inside the functions and returned.
Example, where it might be an issue:
const aggregator = (aggregateData) => {
for (const i = 0; i < 5; i++)
aggregateData = [...aggregateData, i];
return aggregateData;
}
const example = () => {
let arr = [];
let data = aggregator(arr);
// if this array is passed to couple other functions too and mutated then it will get hard to track. This is where the issue can occur
}
example();