Lesson 47 of 80
sort() and splice() have quietly ruined more state than any other array methods, because they mutate in place. Their new -to- prefixed twins do the same job and hand you a new array instead.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
.sort(), .reverse(), .splice(), and index assignment (arr[i] = x) all mutate the array they're called on — they change it in place and return either the same array or nothing useful. That's exactly the behavior that causes bugs in React state, Redux reducers, and anywhere else you're not supposed to mutate a value you don't own: you call .sort() on what you thought was a fresh copy, and it turns out to be a reference to state that three other components are also reading.
toSorted(), toReversed(), toSpliced(), and with() are the same four operations, but each one leaves the original array untouched and returns a new array with the result. Same logic, opposite contract.
const [items, setItems] = useState([
{ id: 1, name: "Zebra" },
{ id: 2, name: "Apple" },
{ id: 3, name: "Mango" },
]);
// The mutating way — this is the classic React bug:
function sortBroken() {
items.sort((a, b) => a.name.localeCompare(b.name)); // mutates `items` in place
setItems(items); // same array reference — React sees no change, doesn't re-render
}
// The non-mutating way:
function sortFixed() {
setItems(items.toSorted((a, b) => a.name.localeCompare(b.name))); // new array
}
sortBroken mutates items and then calls setItems with that same reference. React compares old and new state by reference, sees they're identical, and may skip the re-render entirely — the array is sorted in memory, but the screen never updates, and any other code still holding the old items reference now sees mutated data it never asked for. sortFixed sidesteps the whole problem: toSorted builds a new array, the original stays exactly as it was, and React correctly sees a new reference and re-renders.
The other three follow the same pattern:
const original = [10, 20, 30, 40];
original.with(1, 99); // [10, 99, 30, 40] — original untouched
original.toReversed(); // [40, 30, 20, 10] — original untouched
original.toSpliced(1, 2, 0); // [10, 0, 40] — original untouched
original; // still [10, 20, 30, 40]
with(index, value) is the immutable answer to arr[index] = value — replace one element, get a new array back, without ever writing through the original index.
.map() or .filter(), which were already non-mutating. Only the specific methods that used to mutate — sort, reverse, splice, and index assignment — needed a non-mutating twin. Don't go looking for toMapped(); it doesn't exist because .map() never mutated in the first place.toSpliced takes the same arguments as splice, but doesn't return the removed elements. splice returns the deleted items (useful when you're removing and want them); toSpliced returns the resulting array instead, since that's the thing you actually asked for.structuredClone or [...arr].sort() remain the fallback until your baseline moves up.The array-sort-and-stability lesson covers what .sort()'s comparator does and why its stability guarantee matters — that logic is identical here, since toSorted uses the exact same comparator and the exact same stable algorithm underneath. The only thing that changed is who owns the result: mutate-in-place versus hand-back-something-new is the same tension immutability and pure functions cover more generally, applied to four specific, previously-mutating array methods.