Mutable vs Immutable, Side by Side
In the mutable example, ref and arr point to the exact same array in memory — changing one changes what the other sees. In the immutable example, newArr is a completely separate array; the original is never touched.
Why React Cares About This So Much
React often decides whether to re-render using a fast reference comparison (oldState === newState) rather than deeply comparing every property. If you mutate state in place, the reference never changes — React sees the same object and assumes nothing happened, so it skips the re-render even though the data is actually different.
const Does Not Mean Immutable
This is one of the most common misconceptions in JavaScript:
| Code | What it actually means |
|---|---|
const x = 5; | x cannot be reassigned |
const arr = [1,2,3]; arr.push(4); | Allowed! const only locks the variable binding, not the array's contents |
Object.freeze(obj) | Actually prevents top-level property changes (but not nested objects) |
What's Mutable and What's Immutable in JavaScript
| Type | Mutable? |
|---|---|
| String | No — immutable; methods like .toUpperCase() return a new string |
| Number, Boolean | No — immutable primitives |
| Array | Yes — .push(), .splice(), .sort() all mutate in place |
| Object | Yes — properties can be added/changed/deleted directly |
Immutability and Pure Functions
A pure function never mutates its inputs and always returns the same output for the same input. Immutability makes writing pure functions natural, because there's no shared mutable state to accidentally corrupt — this is also why immutability is central to functional programming and to predictable state management in Redux.
⚠️ The Performance Trade-off
Always creating new objects/arrays costs more memory and CPU than mutating in place — especially for large, deeply nested state. Libraries like Immer and Immutable.js use structural sharing (reusing the parts of the structure that didn't change) to keep immutable updates close to mutable performance.
💡 Why It's Worth the Cost
Immutability makes undo/redo, time-travel debugging, and detecting "did anything actually change?" trivial — you just compare references instead of deep-diffing entire object trees. That predictability is usually worth far more than the small extra allocation cost.
How We Research and Update This Guide
We test the underlying formula or workflow, compare outputs with reliable references, and revise examples whenever the page content changes.
- The workflow or formula is tested directly in the tool and compared against independent reference examples.
- Examples are kept practical so readers can verify the result without hidden assumptions.
- Pages are revised whenever the interface, calculation flow, or surrounding guidance materially changes.
Frequently Asked Questions — Mutability vs Immutability
A mutable value can be changed in place after it is created — the same object in memory gets its contents altered. An immutable value cannot be changed once created — any "modification" actually produces a brand new value, leaving the original untouched. In JavaScript, arrays and objects are mutable by default; strings and numbers are immutable.
React decides whether to re-render a component by comparing the previous state/props to the new ones, often using a fast reference check (===) rather than a deep comparison. If you mutate an object in place, the reference stays the same, so React thinks nothing changed and skips the re-render — causing stale UI bugs. Always creating a new object/array for state updates (e.g. spreading into a new object) ensures React detects the change.
No. const only prevents reassigning the variable itself — it does not make the value immutable. const arr = [1,2,3] stops you from doing arr = [4,5,6], but arr.push(4) still works because the array's contents are mutable. To truly freeze an object's top-level properties, you need Object.freeze() — and even that does not deep-freeze nested objects.
Mutating in place is generally faster and uses less memory because no new object is allocated. Immutable updates create new objects/arrays on every change, which costs more memory and CPU time for large structures. Libraries like Immutable.js and Immer use structural sharing — reusing unchanged parts of the data structure — to make immutable updates close to mutable performance for large nested state.
A pure function always returns the same output for the same input and has no side effects — it does not mutate its arguments or any external state. Pure functions and immutability go hand in hand: a function that never mutates its inputs is far easier to reason about, test, and run safely in parallel, since there is no shared mutable state to create race conditions.
Because immutable values never change after creation, you can safely keep a reference to a past state and compare it to the current one — this is exactly how undo/redo features and React DevTools' time-travel debugging work. With mutable data, by the time you go to inspect an old reference, it may have already been changed in place, making historical comparison unreliable.