Why Object.assign Can Break Your React Apps and How Spread Syntax Saves You
This article explains the hidden pitfalls of JavaScript's Object.assign—mutating the target object and performing only shallow copies—illustrates the bugs they can cause in modern frontend frameworks, and shows how the spread operator (or structuredClone) provides a safer, more readable alternative.
For many years, Object.assign() has been our trusty helper, but as JavaScript evolves, its drawbacks become more evident.
The Starting Point
Before ES6, merging objects usually required manually iterating properties or using library functions such as jQuery.extend. The introduction of Object.assign() was a huge improvement, with basic usage like:
const target = { a: 1, b: 1 };
const source = { b: 2, c: 2 };
const result = Object.assign(target, source);
console.log(target); // { a: 1, b: 2, c: 2 }
console.log(result); // { a: 1, b: 2, c: 2 }
console.log(target === result); // trueIt copies all enumerable own properties from one or more source objects to a target object and returns the target. Looks good, right? But the devil is in the details.
Two Major "Sins" of Object.assign
The problem with Object.assign is not that it can't be used, but that it is easy to misuse, leading to hard‑to‑track bugs.
1. Mutability: Unexpected Side Effects
Object.assignmutates its first argument (the target object).
In the example above, the target object is directly modified, which is a huge risk in modern frontend frameworks that favor immutability, such as React or Vue.
To avoid mutating the original object, developers often resort to a more verbose pattern:
This approach works but feels cumbersome.
2. Shallow Copy: Deep Object Pitfall
This is the most fatal flaw of Object.assign: it performs a shallow copy, not a deep copy. If a property value is itself an object, only the reference is copied.
Consider the following example:
const user = { details: { age: 30 } };
const updatedInfo = { details: { city: 'London' } };
const updatedUser = Object.assign({}, user, updatedInfo);
// Problem appears
updatedUser.details.age = 31; // The original object's nested property is also changed!
console.log(user.details.age); // 31 <-- unintended side effectSolution: Spread Syntax
The object spread syntax is superior in almost every way to Object.assign and has become the community's preferred way to merge objects.
It is designed to create new objects, perfectly aligning with immutability principles.
The code is not only more concise and readable but also fundamentally avoids the risk of mutating the original object.
Let's rewrite the nested‑object example using spread syntax:
const updatedUser = {
...user,
details: {
...user.details,
// manually spread nested objects
...updatedDetails
}
};
updatedUser.details.age = 31;
console.log(user.details.age); // 30 (safe!)By manually spreading the nested details object, we avoid reference issues. Although it adds an extra step compared to Object.assign, the data flow becomes clear.
If you don't want to manually spread nested objects, use structuredClone for a deep copy: { ...structuredClone(user), updatedInfo } .
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaScript
Provides JavaScript enthusiasts with tutorials and experience sharing on web front‑end technologies, including JavaScript, Node.js, Deno, Vue.js, React, Angular, HTML5, CSS3, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
