AUR0227
Error Message
AUR0227: Computed mutating
Description
This error occurs when a computed property attempts to modify (mutate) observable state during its computation. Computed properties should be pure functions that only read values and return a result without causing side effects or mutations.
Why This Matters
Computed properties are designed to be:
Pure: Only read values, never modify them
Deterministic: Always return the same result for the same inputs
Side-effect free: Don't trigger other state changes
When a computed property mutates state, it can cause:
Infinite update loops
Unpredictable behavior
Performance issues
Hard-to-debug race conditions
Common Scenarios
Direct Property Mutation
export class MyComponent {
items = [1, 2, 3];
counter = 0;
// ❌ Wrong: Computed property mutating state
get processedItems() {
this.counter++; // This is a mutation!
return this.items.map(x => x * 2);
}
}
Array/Object Mutations
export class MyComponent {
data = [{ name: 'John' }, { name: 'Jane' }];
// ❌ Wrong: Mutating the original array
get sortedData() {
return this.data.sort((a, b) => a.name.localeCompare(b.name)); // Mutates original!
}
// ❌ Wrong: Modifying objects in computed
get processedData() {
return this.data.map(item => {
item.processed = true; // Mutating original objects!
return item;
});
}
}
Observable Mutations
import { observable } from '@aurelia/runtime';
export class MyComponent {
@observable items = [];
@observable status = 'idle';
// ❌ Wrong: Changing observable state in computed
get itemCount() {
if (this.items.length === 0) {
this.status = 'empty'; // This is a mutation!
}
return this.items.length;
}
}
Solutions
1. Keep Computeds Pure
export class MyComponent {
items = [1, 2, 3];
// ✅ Correct: Pure computed property
get processedItems() {
// Only reads, doesn't mutate
return this.items.map(x => x * 2);
}
// ✅ Correct: Count without side effects
get itemCount() {
return this.items.length;
}
}
2. Use Non-Mutating Array Methods
export class MyComponent {
data = [{ name: 'John' }, { name: 'Jane' }];
// ✅ Correct: Create new sorted array
get sortedData() {
return [...this.data].sort((a, b) => a.name.localeCompare(b.name));
}
// ✅ Correct: Create new objects instead of mutating
get processedData() {
return this.data.map(item => ({
...item,
processed: true
}));
}
}
3. Move Mutations to Methods or Effects
import { observable, computed } from '@aurelia/runtime';
export class MyComponent {
@observable items = [];
@observable status = 'idle';
// ✅ Correct: Pure computed
get itemCount() {
return this.items.length;
}
// ✅ Correct: Use method for mutations
updateStatus() {
this.status = this.items.length === 0 ? 'empty' : 'has-items';
}
// ✅ Correct: React to changes in lifecycle or watchers
itemsChanged() {
this.updateStatus();
}
}
4. Use Effects for Side Effects
import { observable, IObservation } from '@aurelia/runtime';
export class MyComponent {
@observable items = [];
@observable status = 'idle';
@observable lastUpdated: Date;
constructor(private observation: IObservation) {}
bound() {
// ✅ Correct: Use effect for side effects
this.observation.run(() => {
// This runs when items.length changes
if (this.items.length === 0) {
this.status = 'empty';
} else {
this.status = 'has-items';
}
this.lastUpdated = new Date();
});
}
// ✅ Correct: Pure computed
get itemCount() {
return this.items.length;
}
}
Example: Refactoring Problematic Code
// ❌ Before: Computed with mutations
export class ShoppingCart {
@observable items = [];
@observable total = 0;
@observable itemCount = 0;
// ❌ Wrong: Mutations in computed
get cartSummary() {
this.total = this.items.reduce((sum, item) => sum + item.price, 0);
this.itemCount = this.items.length;
return `${this.itemCount} items - $${this.total}`;
}
}
// ✅ After: Pure computed with separate mutations
export class ShoppingCart {
@observable items = [];
// ✅ Correct: Pure computeds
get total() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
get itemCount() {
return this.items.length;
}
get cartSummary() {
return `${this.itemCount} items - $${this.total}`;
}
// ✅ Correct: Mutations in methods
addItem(item) {
this.items.push(item);
}
removeItem(index) {
this.items.splice(index, 1);
}
}
Debugging Tips
Review Computed Logic: Ensure computed properties only read and calculate
Check for Assignments: Look for any
=
assignments within computed gettersVerify Array Methods: Use non-mutating methods like
[...array].sort()
instead ofarray.sort()
Move Side Effects: Put mutations in methods, lifecycle hooks, or effects
Use DevTools: Monitor observable changes to identify unexpected mutations
Related Errors
Last updated
Was this helpful?