← Retour au blog
Angular Signalsreactive stateperformance optimizationcomputedstate management

Angular Signals : quand utiliser computed() plutôt que la dérivation d'état

Les Signals d'Angular 16+ ont radicalement changé la manière de gérer l'état réactif. Mais avec l'introduction de computed() et les patterns de dérivation d'état (state derivation), beaucoup de développeurs confondent les deux approches. La différence n'est pas qu'une question de syntaxe : elle impacte directement la performance, la prévisibilité du code et la complexité à maintenir. Computed() est une fonction pure qui recalcule son résultat uniquement quand ses dépendances changent. La dérivation d'état, elle, représente plutôt un pattern où vous contrôlez manuellement quand et comment transformer votre état source. Comprendre cette distinction est crucial pour construire des applications Angular réactives sans surcharge.

Computed() : la pureté garantie et l'optimisation automatique

Computed() est une fonction qui prend en paramètre une fonction de calcul pure et retourne un signal en lecture seule. Sa force réside dans sa simplicité et son optimisation automatique. Quand vous créez un computed signal, Angular suit toutes les dépendances (autres signals, computed, etc.) lues à l'intérieur de votre fonction. Si aucune dépendance n'a changé, le résultat est mis en cache et la fonction ne s'exécute pas. C'est transparent et très efficace pour les calculs légers à moyens. Voici un exemple concret : vous avez un signal utilisateur et un signal de liste de commandes, et vous voulez afficher le montant total des commandes de cet utilisateur. Au lieu de recalculer à chaque changement d'une autre variable, computed() s'exécute uniquement si l'utilisateur ou la liste de commandes change.

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);

});

// Accès au résultat

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

Le computed est idéal quand votre transformation est déterministe : même entrées = même sortie, toujours. Pas d'effets secondaires, pas d'appels API, pas de dépendances externes non-signalées. Angular peut optimiser agressivement parce qu'il sait que le résultat ne dépend que de ce qui est lu à l'intérieur.

Dérivation d'état : quand vous contrôlez le flux

La dérivation d'état est un pattern plus large où vous transformez votre état source en nouvel état, mais avec plus de contrôle et souvent plus de logique métier. Au lieu d'utiliser computed(), vous pourriez utiliser effect() pour mettre à jour un autre signal dépendant, ou même utiliser des observables avec toSignal(). La dérivation d'état brille quand votre logique n'est pas pure : vous avez besoin d'un side-effect (log, mutation, appel asynchrone), ou vous avez besoin de conditions plus complexes pour décider quand mettre à jour.

Prenons un exemple : vous voulez dériver un état "filtre appliqué" en fonction de critères utilisateur, mais aussi logger chaque changement et potentiellement déclencher une analyse. Avec un effet, vous avez le contrôle total :

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);

// Ici vous pouvez aussi faire un appel API, une mutation

appliedFilter.set(newFilter);

});

C'est plus verbeux que computed(), mais vous avez la liberté d'ajouter de la logique métier, de gérer l'asynchrone proprement, ou de contrôler précisément quand la mise à jour se déclenche.

Cas d'usage réels et limites

En pratique, computed() est votre premier choix pour 80% des transformations d'état. Filtrer une liste, mapper des données, formater une chaîne, agréger des valeurs, vérifier une condition sur l'état : tout cela s'exprime naturellement avec computed(). Son optimisation est automatique et fiable. Les problèmes apparaissent quand vous essayez de forcer un computed() à faire du travail impure. Si votre fonction de calcul a des side-effects (même mineurs comme un console.log), vous perdez la garantie de cache et Angular ne peut plus optimiser. Pire : les side-effects s'exécutent plusieurs fois, à des moments imprévisibles.

La dérivation d'état via effect() devient nécessaire pour les flux asynchrones (chargement de données depuis une API quand un filter change), pour les mutations complexes (mise à jour d'une store NgRx, sauvegarde locale), ou quand votre transformation dépend d'état externe (configuration globale, date courante). L'exemple classique : vous avez un signal de recherche, et vous voulez déclencher un appel API avec debounce. Vous ne pouvez pas faire cela dans un computed() pur. Vous utilisez un effet avec un debounceTime() ou une logique manuelle.

Le piège courant : computed() pour tout

Beaucoup de développeurs, séduits par la simplicité de computed(), essaient de l'utiliser partout, y compris pour des cas qui demandent vraiment un effet. Le symptôme : vous avez un computed() qui fait un appel API, ou qui loggue, ou qui modifie un autre signal de manière imprévisible. Votre application commence à avoir des comportements étranges, des re-exécutions non-contrôlées, des appels API dupliqués. Le deuxième piège est d'imbriquer trop de computed() les uns dans les autres sans réfléchir à la dépendance finale. Si vous avez computed A qui dépend du signal X, computed B qui dépend de A, et computed C qui dépend de B, et que vous en lisez un quatrième qui dépend de C, vous avez créé une chaîne de dépendances qui, bien qu'optimisée, est difficile à déboguer. L'arbre de dépendances devient opaque.

Composition et lisibilité

Une bonne stratégie est de garder vos computed() simples et ciblés : une transformation par computed(). Si votre logique métier est complexe, décomposez en plusieurs computed() plutôt qu'un seul géant. Nommez-les clairement pour que la dépendance soit explicite. Pour la dérivation d'état, préférez un effet nommé et bien commenté : un effet par responsabilité. Si vous avez besoin de synchroniser plusieurs effets ou de gérer un flux complexe, envisagez un service dédié avec des méthodes claires plutôt que de disséminer la logique dans des effets partout.

Conclusion opérationnelle

Computed() est votre outil par défaut pour transformer l'état : c'est pur, optimisé, et prévisible. Utilisez-le pour les filtres, les mappages, les agrégations, les dérivations simples. La dérivation d'état via effect() est votre outil pour les flux complexes, l'asynchrone, et la logique métier impure. La règle simple : si votre transformation peut être écrite comme une fonction pure avec un return, utilisez computed(). Sinon, utilisez un effet. Cette distinction clarifie votre code, améliore la performance et rend le debugging beaucoup moins frustrant.

Développeur Angular & Mobile freelance — Strasbourg.

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