Signal Immutability Guidelines
Immutability is crucial for predictable signal behavior and optimal change detection. Always create new references when updating state.
Why Immutability Matters
import { signal, computed } from '@angular/core';
// ❌ Bad: Mutation doesn't trigger updates
const items = signal([1, 2, 3]);
const count = computed(() => items().length);
items().push(4); // Mutates array, count doesn't update!
console.log(count()); // Still 3
// ✅ Good: Create new array
items.set([...items(), 4]);
console.log(count()); // Now 4
Array Updates
const items = signal<number[]>([1, 2, 3]);
// ✅ Add item
items.update(arr => [...arr, 4]);
// ✅ Remove item
items.update(arr => arr.filter(x => x !== 2));
// ✅ Update item
items.update(arr => arr.map(x => x === 2 ? 20 : x));
// ✅ Sort (creates new array)
items.update(arr => [...arr].sort());
Object Updates
interface User {
name: string;
age: number;
address: { city: string; };
}
const user = signal<User>({
name: 'Alice',
age: 30,
address: { city: 'NYC' }
});
// ✅ Update top-level property
user.update(u => ({ ...u, age: 31 }));
// ✅ Update nested property
user.update(u => ({
...u,
address: { ...u.address, city: 'LA' }
}));
Helper Functions
// Immutable array helpers
const arrayHelpers = {
add: <T>(arr: T[], item: T) => [...arr, item],
remove: <T>(arr: T[], index: number) => arr.filter((_, i) => i !== index),
update: <T>(arr: T[], index: number, item: T) =>
arr.map((x, i) => i === index ? item : x)
};
// Usage
const items = signal<number[]>([1, 2, 3]);
items.update(arr => arrayHelpers.add(arr, 4));
Key Takeaways
- Never mutate signal values directly
- Always create new references for arrays/objects
- Use spread operator or helper functions
- Deep updates require nested spreading
Next Steps
- Learn about State Management
- Explore Performance Optimization