Keywords: JavaScript | React | Non-extensible Objects | State Management | Object Copying
Abstract: This article provides an in-depth analysis of the 'object is not extensible' error in JavaScript, focusing on React state management scenarios. It explores the differences between shallow and deep copying of arrays, compares various solution approaches, and emphasizes best practices using object spread syntax for safe state modifications.
Problem Background and Error Analysis
In JavaScript development, particularly when working with state management in React, developers frequently encounter the TypeError: Cannot add property selected, object is not extensible error. This error occurs when attempting to add new properties to objects that have been marked as non-extensible.
In the original problematic code:
export const initSelect = (data) => {
let newData = data.concat();
newData.map((item) => {
item.selected = false;
})
return newData;
}Although data.concat() creates a shallow copy of the array, the object references within the array still point to the original objects. When these objects are marked as non-extensible by React or other libraries, direct modification triggers the error.
Mechanisms of Non-extensible Objects
JavaScript provides several methods to control object extensibility:
Object.preventExtensions(): Prevents adding new propertiesObject.seal(): Prevents adding/removing propertiesObject.freeze(): Completely freezes the object, making it immutable
In strict mode, adding properties to non-extensible objects throws a TypeError, while in non-strict mode, the operation is silently ignored. React state management libraries often use these methods to ensure state immutability.
Optimal Solution
Based on the best-rated answer (score 10.0), the recommended solution is:
export const initSelect = (data) => {
return data.map((item) => ({
...item,
selected: false
}));
}The key advantages of this approach include:
- Using spread syntax
...to create new object instances - Preserving all existing properties from the original objects
- Safely adding the new
selectedproperty - Returning a completely new array without affecting original data
Alternative Approaches Comparison
The alternative solution (score 3.6) uses Object.assign:
let newData = data.map((item) =>
Object.assign({}, item, {selected:false})
)While this approach also solves the problem, compared to spread syntax:
- The syntax is more verbose
- There might be slight performance differences
- Readability is less intuitive than spread syntax
Practical Application Scenarios
In React development, this pattern is particularly useful for:
- Managing selection states in list items
- Initializing form data
- Transforming and enhancing state data
- Implementing immutable data patterns
By properly employing object copying techniques, developers can avoid state pollution and ensure application reliability and maintainability.