Angular Signals made state management simpler.
But here’s the problem:
Most developers are using signals… like they used RxJS or old Angular state.
And that defeats the whole purpose.
Let’s break down the most common mistakes — and how to fix them.
⚠️ The Core Issue
Signals are not just a new API.
They require a different mental model:
- signal() → state
- computed() → derived state
- effect() → side effects
If you mix these roles… things get messy fast.
❌ Mistake 1: Using effect() for Derived State
effect(() => {
this.fullName.set(
${this.firstName()} ${this.lastName()}
);
});
🚨 Problem:
- unnecessary state
- extra updates
- harder to debug
✅ Fix: Use computed()
fullName = computed(() =>
${this.firstName()} ${this.lastName()}
);
👉 No duplication
👉 No syncing
👉 Automatically reactive
❌ Mistake 2: Treating Signals Like Variables
this.count = this.count + 1;
🚨 Signals don’t work like normal variables.
✅ Fix
this.count.update(c => c + 1);
👉 Signals are reactive containers — not plain values
❌ Mistake 3: Overusing Effects Everywhere
effect(() => {
if (this.count() > 10) {
this.isLarge.set(true);
}
});
🚨 This is not a side effect — it’s derived logic.
✅ Fix
isLarge = computed(() => this.count() > 10);
👉 Cleaner
👉 Predictable
👉 No extra updates
❌ Mistake 4: Creating Too Many Signals
firstName = signal('');
lastName = signal('');
fullName = signal('');
isValid = signal(false);
🚨 Too much state = harder to manage
✅ Fix
Only store source of truth
firstName = signal('');
lastName = signal('');
fullName = computed(() =>
${this.firstName()} ${this.lastName()}
);
👉 Derive everything else
❌ Mistake 5: Mixing RxJS and Signals Without Strategy
this.userService.getUser().subscribe(user => {
this.user.set(user);
});
This works… but:
🚨 Problems:
unclear ownership
mixed patterns
harder debugging
✅ Better Approach
Be intentional:
use RxJS for async streams
convert to signals at boundaries
user = toSignal(this.userService.getUser());
👉 Clean separation
❌ Mistake 6: Putting Too Much Logic in Templates
{{ user()?.firstName + ' ' + user()?.lastName }}
🚨 Recomputed frequently
🚨 hard to maintain
✅ Fix
fullName = computed(() =>
${this.user()?.firstName} ${this.user()?.lastName}
);
{{ fullName() }}
🧠 The Correct Mental Model
Instead of thinking:
“When something changes, update everything manually”
Think:“Define relationships — let Angular handle updates”
⚙️ When to Use What
Use Case Tool
Store state signal()
Derive values computed()
Side effects effect()
🚀 Why This Matters
Misusing signals leads to:
- unnecessary re-renders
- duplicated state
- complex logic
- harder debugging
Using them correctly gives you:
- predictable data flow
- better performance
- cleaner code
🔥 Final Thought
Signals are powerful — but only if used correctly.
Most bugs don’t come from the framework.
They come from:
using the right tools in the wrong way
💥 One Line to Remember
If you’re using effect() to manage state… you’re probably doing it wrong.
Master the mental model — and Angular becomes significantly simpler.
Top comments (0)