← Retour au blog
Angular Signalsreactive stateperformance optimizationcomputedstate management

Angular Signals: When to Use computed() Over State Derivation

Angular 16+ Signals fundamentally changed how we manage reactive state. But with the introduction of computed() and state derivation patterns, many developers conflate the two approaches. The difference is not just syntax—it directly impacts performance, code predictability, and maintenance complexity. Computed() is a pure function that recalculates its result only when dependencies change. State derivation, by contrast, is a broader pattern where you manually control when and how to transform your source state. Understanding this distinction is crucial to building reactive Angular applications without overhead.

Computed(): Guaranteed Purity and Automatic Optimization

Computed() is a function that takes a pure calculation function and returns a read-only signal. Its strength lies in simplicity and automatic optimization. When you create a computed signal, Angular tracks all dependencies (other signals, computeds, etc.) read inside your function. If no dependency changed, the result is cached and the function doesn't execute. This is transparent and highly efficient for light-to-medium computations. Here's a concrete example: you have a user signal and an orders list signal, and you want to display the total amount of orders for that user. Instead of recalculating on every unrelated variable change, computed() executes only if the user or orders list changes.

import { signal, computed } from '@angular/core';

const user = signal({ id: 1, name: 'Alice' });

const orders = signal([

{ userId: 1, amount: 50 },

{ userId: 1, amount: 120 },

{ userId: 2, amount: 30 }

]);

const totalUserOrders = computed(() => {

const currentUser = user();

const currentOrders = orders();

return currentOrders

.filter(o => o.userId === currentUser.id)

.reduce((sum, o) => sum + o.amount, 0);

});

// Access the result

console.log(totalUserOrders()); // 170

Computed is ideal when your transformation is deterministic: same inputs always yield the same output. No side effects, no API calls, no undeclared external dependencies. Angular can optimize aggressively because it knows the result depends only on what's read inside.

State Derivation: When You Control the Flow

State derivation is a broader pattern where you transform your source state into new state, but with more control and often more business logic. Instead of computed(), you might use effect() to update another dependent signal, or even use observables with toSignal(). State derivation shines when your logic isn't pure: you need a side-effect (logging, mutation, async call), or you need more complex conditions to decide when to update.

Take an example: you want to derive an "applied filter" state based on user criteria, but also log each change and potentially trigger analytics. With an effect, you have full control:

const filterCriteria = signal({ category: 'all', priceMax: 1000 });

const appliedFilter = signal<any>(null);

effect(() => {

const criteria = filterCriteria();

const newFilter = {

...criteria,

timestamp: Date.now()

};

console.log('Filter applied:', newFilter);

// Here you can also make an API call, a mutation

appliedFilter.set(newFilter);

});

It's more verbose than computed(), but you have the freedom to add business logic, handle async properly, or precisely control when the update triggers.

Real-World Use Cases and Limits

In practice, computed() is your first choice for 80% of state transformations. Filtering a list, mapping data, formatting a string, aggregating values, checking a condition on state: all express naturally with computed(). Its optimization is automatic and reliable. Problems surface when you force a computed() to do impure work. If your calculation function has side-effects (even minor ones like a console.log), you lose the cache guarantee and Angular can no longer optimize. Worse: side-effects execute multiple times, at unpredictable moments.

State derivation via effect() becomes necessary for async flows (loading data from an API when a filter changes), complex mutations (updating an NgRx store, local persistence), or when your transformation depends on external state (global config, current date). The textbook example: you have a search signal and want to trigger an API call with debounce. You can't do that in a pure computed(). You use an effect with debounceTime() or manual logic.

The Common Pitfall: computed() for Everything

Many developers, seduced by computed()'s simplicity, try to use it everywhere, including cases that genuinely need an effect. The symptom: you have a computed() making an API call, logging, or mutating another signal unpredictably. Your app starts exhibiting strange behaviors, uncontrolled re-executions, duplicate API calls. The second pitfall is nesting too many computeds into each other without thinking through the final dependency. If computed A depends on signal X, computed B depends on A, computed C depends on B, and you read a fourth computed that depends on C, you've created a dependency chain that, while optimized, is hard to debug. The dependency tree becomes opaque.

Composition and Readability

A good strategy is keeping your computeds simple and targeted: one transformation per computed. If your business logic is complex, break it into multiple computeds rather than one giant. Name them clearly so the dependency is explicit. For state derivation, prefer a named and well-commented effect: one effect per responsibility. If you need to synchronize multiple effects or manage a complex flow, consider a dedicated service with clear methods rather than scattering logic across effects everywhere.

Operational Conclusion

Computed() is your default tool for transforming state: it's pure, optimized, and predictable. Use it for filters, mappings, aggregations, simple derivations. State derivation via effect() is your tool for complex flows, async, and impure business logic. The simple rule: if your transformation can be written as a pure function with a return, use computed(). Otherwise, use an effect. This distinction clarifies your code, improves performance, and makes debugging far less frustrating.

Développeur Angular & Mobile freelance — Strasbourg.

© 2026 Emilien Pons — Tous droits réservés.Conçu avec Angular, PrimeNG et ❤️