This content originally appeared on DEV Community and was authored by LcsGa
Récemment, on voit de nombreux développeurs tenter de remplacer RxJS en créant leurs propres “opérateurs” pour les signals d’Angular. Cette démarche est compréhensible, mais elle peut entraîner des erreurs subtiles. Pour les éviter, il est essentiel de bien comprendre la distinction entre deux modèles de réactivité : Push et Pull.
Push (Observables) : Chaque donnée poussée compte
Dans le modèle Push, l’émetteur (la source) est aux commandes. Il émet des valeurs dans un flux “observable”, et chaque valeur émise existe indépendamment. Ces valeurs sont ensuite traitées, une par une, par ceux qui les observent.
Imaginez une chaîne de montage : chaque pièce qui passe existe indépendamment. Si vous vous placez le long de la chaîne pour observer, vous verrez chaque pièce passer, sans exception. Si vous arrivez en retard, vous avez simplement raté les premières pièces. Elles ont été envoyées, mais elles ne vous attendent pas.
Ce modèle est idéal pour gérer des flux de données, où chaque valeur a son importance.
Pull (Signals) : Seule la dernière valeur tirée compte
Dans le modèle Pull, c’est le consommateur qui est aux commandes. Une valeur n’a d’intérêt que lorsqu’elle est explicitement lue. Le consommateur “tirera” la valeur au moment où il en a besoin. Cela implique que les valeurs transitoires qui ne sont pas lues n’ont aucune importance.
Imaginez un tableau d’affichage numérique dans une gare. Il est constamment mis à jour, affichant des informations en temps réel sur les trains. Il peut y avoir des dizaines de mises à jour par seconde, mais le voyageur ne verra que la dernière version affichée lorsqu’il lève les yeux. Les mises à jour intermédiaires ne comptent pas pour lui. Seule la dernière version de l’état du tableau est pertinente.
Les signals sont donc parfaits pour gérer un état dont seule la dernière version compte.
Pourquoi certains opérateurs ont du sens et d’autres non
Cette distinction entre Push et Pull est cruciale pour comprendre pourquoi certains opérateurs fonctionnent avec les signals et d’autres non.
Ce qui fonctionne : opérer sur l’état final.
Les opérateurs qui ne se préoccupent pas de l’historique et réagissent uniquement au changement d’état final fonctionneront bien avec les signals. Par exemple, un opérateur double
est parfait pour un signal. Il lit simplement la valeur actuelle, la multiplie par deux et retourne le résultat. Il n’a besoin de rien d’autre.
const count = signal(2);
const doubledCount = computed(() => count() * 2);
console.log(doubledCount()); // 4
Ce qui ne fonctionne pas : avoir besoin de l’historique.
Les opérateurs comme filter
ou take
sont conçus pour travailler avec des flux de données. Les adapter aux signals est risqué. Pourquoi ?
-
Le piège de
filter
:Imaginez un signal initialisé à
1
(const counter = signal(1)
) et un signal résultant (uncomputed
par exemple) qui ne garde que les nombres pairs. Si vous faitescounter.set(2)
, puiscounter.set(3)
, le signal résultant ne verra jamais la valeur2
. En effet, si vous ne tirez que la valeur finale, le signal prendra uniquement en compte la dernière valeur, qui est3
(impair), et la filtrera. Ainsi, le résultat ne sera jamais celui que vous attendiez.
const counter = signal(1); const evenCounter = computed(() => { const value = counter(); return isEven(value) ? value : undefined; }); counter.set(2); counter.set(3); console.log(evenCounter()); // undefined => instead of 2
À l’inverse, si la dernière valeur est
4
, vous aurez la fausse impression que votre opérateur fonctionne correctement.
const counter = signal(1); const evenCounter = computed(() => { const value = counter(); return isEven(value) ? value : undefined; }); counter.set(2); counter.set(3); counter.set(4); console.log(evenCounter()); // 4 => tricky result
-
Le piège de
take
:De la même manière, avec le même signal
counter
initialisé à1
, si vous effectuezcounter.set(2)
, puiscounter.set(3)
, puiscounter.set(4)
, un opérateurtake(3)
ne prendra en compte que la dernière valeur (4
). Il n’aura aucune idée que les valeurs1
,2
et3
ont existé. Pour lui, c’est sa première (et unique) itération.
En résumé
Les signals sont optimisés pour gérer l’état de l’application (modèle Pull), tandis que RxJS est parfait pour gérer des flux de données (modèle Push).
Avant d’utiliser l’un ou l’autre, posez-vous la question suivante :
“Ai-je besoin de connaître l’historique des données, ou est-ce que seule la dernière valeur m’importe ?”
Si seule la dernière valeur compte, les signals sont faits pour vous. Sinon, un observable est probablement la meilleure solution.
This content originally appeared on DEV Community and was authored by LcsGa