DEV Community

Cover image for You’re Using Angular Signals Wrong (Almost Everyone Is)
Mridu Dixit
Mridu Dixit

Posted on

You’re Using Angular Signals Wrong (Almost Everyone Is)

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)