RxJS gère les flux d'événements asynchrones sous forme d'Observables. Concepts clés, différence avec les Promises, quand l'utiliser avec React et exemples de code à jour.
RxJS est une bibliothèque JavaScript de programmation réactive : elle représente les événements (clics, frappes au clavier, réponses d’API, messages WebSocket) sous forme de flux appelés Observables, que l’on filtre, transforme et combine avec des opérateurs. Avec React, elle est utile pour les flux asynchrones complexes : recherche avec temporisation, annulation des requêtes obsolètes, combinaison de plusieurs sources ou données en temps réel. Pour un simple état de composant ou un appel d’API isolé, les outils natifs de React suffisent. La version stable actuelle est la 7.8.2.
Ce guide explique les concepts de RxJS (Observable, Observer, opérateurs, Subject), la différence avec les Promises, quand l’utiliser dans une application React et quand s’en passer, puis montre du code à jour : un hook de recherche, la combinaison de requêtes, la bibliothèque React-RxJS et les tests.
- Qu’est-ce que RxJS ?
- Que signifie « flux de données asynchrone » ?
- Qu’est-ce qu’un Observable ?
- Quelle différence entre un Observable et une Promise ?
- Quels opérateurs RxJS connaître en priorité ?
- Pourquoi utiliser RxJS avec React ?
- Quand ne pas utiliser RxJS ?
- Comment utiliser RxJS dans un composant React ?
- Comment combiner plusieurs requêtes ?
- Que propose la bibliothèque React-RxJS ?
- RxJS ou MobX ?
- Comment tester un flux RxJS ?
- Où en est RxJS aujourd’hui ?
- Questions fréquentes
- Sources
Qu’est-ce que RxJS ?
RxJS (Reactive Extensions for JavaScript) est la version JavaScript de ReactiveX, une famille de bibliothèques qui combine le patron de conception Observer, le patron Iterator et la programmation fonctionnelle pour gérer des séquences d’événements. Sa documentation la définit comme une bibliothèque pour écrire des programmes asynchrones et basés sur des événements à l’aide de séquences observables.
Quand on parle d’asynchronisme, on pense d’abord aux appels HTTP. Mais une réponse de serveur n’est qu’un événement parmi d’autres qui surviendra « à un moment donné », comme un clic ou une saisie. Prenons un exemple : compter les clics d’un utilisateur est simple (une variable incrémentée à chaque clic). Calculer le temps écoulé entre deux clics, ignorer les doubles clics trop rapides et réinitialiser le compteur après une pause devient vite laborieux avec des variables et des callbacks. RxJS traite tous ces événements comme un flux, un peu comme un tableau dont les éléments arrivent au fil du temps, et fournit des outils pour le filtrer, le transformer et le combiner avec d’autres flux.
Les concepts essentiels :
- Observable : une collection de valeurs ou d’événements futurs, que l’on peut « invoquer » en s’y abonnant.
- Observer : un ensemble de callbacks (
next,error,complete) qui reçoit les valeurs émises par l’Observable. - Subscription (abonnement) : représente l’exécution d’un Observable ; elle sert surtout à l’annuler avec
unsubscribe(). - Opérateurs : des fonctions pures (
map,filter,debounceTime,switchMap…) qui permettent un style fonctionnel pour transformer les flux. - Subject : l’équivalent d’un EventEmitter, le seul moyen de diffuser une même valeur à plusieurs observateurs (multicast).
- Schedulers (planificateurs) : ils contrôlent le moment où les calculs s’exécutent (par exemple avec
setTimeoutourequestAnimationFrame).
Que signifie « flux de données asynchrone » ?
- Flux : une séquence de données disponibles au fil du temps. Le streaming vidéo en est une bonne image : on regarde la vidéo sans attendre la fin du téléchargement.
- Données : des valeurs JavaScript ordinaires (nombres, chaînes, objets).
- Asynchrone : le script lance une opération et continue son exécution sans attendre le résultat, qui sera traité plus tard. Les cas les plus courants sont les requêtes réseau, les événements du DOM et les Promises.
Avec RxJS, ces flux sont représentés par des Observables. Le modèle est dit « push » : c’est la source qui pousse les valeurs vers l’abonné quand elles sont disponibles, à l’inverse du modèle « pull » d’un tableau ou d’un itérateur, où c’est le code consommateur qui va chercher la valeur suivante.
Qu’est-ce qu’un Observable ?
Un Observable est un objet qui émet une séquence de valeurs au fil du temps : les caractères saisis dans un champ, la progression d’un envoi de fichier, les réponses d’un serveur. On lui associe un Observer, c’est-à-dire un objet avec trois fonctions, une par type de notification :
const observer = {
next: (valeur) => { /* traiter chaque valeur */ },
error: (erreur) => { /* gérer l'erreur */ },
complete: () => { /* le flux est terminé */ },
};
const abonnement = monObservable$.subscribe(observer);
// plus tard, pour arrêter d'écouter :
abonnement.unsubscribe();
Trois propriétés à retenir :
- Un Observable est paresseux : tant que personne ne s’y abonne, il ne fait rien. Un Observable qui encapsule une requête HTTP ne l’envoie qu’au moment de l’abonnement (et la renvoie à chaque nouvel abonnement).
- Une erreur ou une complétion termine le flux : après
erroroucomplete, plus aucune valeur n’est émise. - On le crée rarement à la main : on utilise des fonctions de création comme
of,from(à partir d’un tableau ou d’une Promise),fromEvent(événements du DOM),interval,fromFetch(requête HTTP) ou unSubject.
Par convention, on suffixe les variables qui contiennent un Observable par un $ (saisie$, clics$).
Quelle différence entre un Observable et une Promise ?
La comparaison est un bon point de départ, à condition de ne pas la pousser trop loin :
| Critère | Promise | Observable |
|---|---|---|
| Nombre de valeurs | Une seule | Zéro, une ou plusieurs, au fil du temps |
| Démarrage | Immédiat, dès la création | À l’abonnement (paresseux) |
| Annulation | Pas native (il faut un AbortController) | Oui, avec unsubscribe() |
| Transformation | then, async/await | De nombreux opérateurs combinables |
| Exécution des callbacks | Toujours asynchrone | Synchrone ou asynchrone selon la source |
| Standard | Intégrée au langage | Bibliothèque (RxJS) ; une API native est en cours d’expérimentation |
Pour passer d’un Observable à une Promise, RxJS 7 fournit firstValueFrom (première valeur) et lastValueFrom (dernière valeur à la complétion). L’ancienne méthode toPromise() est dépréciée.
Quels opérateurs RxJS connaître en priorité ?
RxJS compte beaucoup d’opérateurs, mais une douzaine couvre l’essentiel des besoins d’une application front-end. On les enchaîne dans la méthode pipe() de l’Observable, chacun recevant le flux produit par le précédent :
| Opérateur | Ce qu’il fait | Exemple d’usage |
|---|---|---|
map | Transforme chaque valeur | Extraire un champ d’une réponse d’API |
filter | Ne laisse passer que les valeurs qui respectent une condition | Ignorer une recherche de moins de deux caractères |
tap | Exécute un effet de bord sans modifier la valeur | Journaliser, mesurer, déclencher un indicateur de chargement |
debounceTime | N’émet qu’après un silence d’une durée donnée | Champ de recherche, sauvegarde automatique |
throttleTime | Émet au plus une valeur par intervalle | Défilement, redimensionnement |
distinctUntilChanged | Ignore une valeur identique à la précédente | Éviter une requête en double |
switchMap | Passe à un nouvel Observable et abandonne le précédent | Recherche : seule la dernière saisie compte |
mergeMap | Lance les Observables internes en parallèle | Envoyer plusieurs fichiers en même temps |
concatMap | Lance les Observables internes l’un après l’autre | Enregistrements qui doivent respecter l’ordre |
combineLatest | Combine les dernières valeurs de plusieurs flux | Filtres d’une liste (catégorie, prix, tri) |
forkJoin | Attend la fin de plusieurs Observables et émet leurs résultats | Charger plusieurs ressources avant d’afficher une page |
catchError et retry | Interceptent une erreur ou relancent la source | Valeur de repli, nouvelle tentative réseau |
takeUntil | Arrête le flux quand un autre Observable émet | Arrêter un glisser-déposer au relâchement de la souris |
En cas de doute, la documentation officielle propose un arbre de décision qui guide vers le bon opérateur à partir de questions simples (« J’ai un Observable existant et je veux… »).
Pourquoi utiliser RxJS avec React ?

React gère très bien l’état et le rendu, mais n’offre pas d’outil dédié aux flux d’événements dans le temps. RxJS comble ce manque dans des cas précis :
- Les recherches et saisies : attendre que l’utilisateur ait fini de taper (
debounceTime), ignorer les valeurs identiques (distinctUntilChanged) et annuler la requête précédente si une nouvelle arrive (switchMap). - Les enchaînements de requêtes : appeler une API, puis plusieurs autres en parallèle à partir du résultat, gérer les erreurs et les nouvelles tentatives (
retry). - Le temps réel : WebSockets, notifications, cours de bourse, positions GPS, avec
webSocketet les opérateurs de fenêtrage (bufferTime,throttleTime). - Les événements à haute fréquence : défilement, redimensionnement, glisser-déposer.
- La communication entre composants éloignés via un Subject partagé, quand remonter l’état ou utiliser un contexte devient lourd.
- Tout ce qui dépend de l’historique des valeurs (et pas seulement de la dernière) : moyennes glissantes, détection de séquences, comparaison avec la valeur précédente.
Quand ne pas utiliser RxJS ?
- La courbe d’apprentissage est raide. Beaucoup de concepts, une API très vaste et un passage d’un style impératif à un style déclaratif. Si votre équipe ne connaît pas RxJS, le coût de maintenance peut dépasser le gain.
- Le débogage est plus difficile. Les piles d’appels traversent le code interne de la bibliothèque, et une longue chaîne d’opérateurs dans un seul
pipese lit mal. L’opérateurtapaide à journaliser chaque étape. - C’est souvent disproportionné. Pour un état local,
useStateetuseReducersuffisent. Pour charger et mettre en cache des données serveur, une bibliothèque spécialisée dans les requêtes est généralement plus simple. - RxJS n’impose aucune architecture. C’est un outil, pas un cadre : inspecter l’état courant de l’application, gérer des valeurs incomplètes ou arrivées dans le désordre, les délais, l’annulation, les nouvelles tentatives, tout cela se résout avec RxJS, mais demande de l’expérience pour être bien conçu.
Comment utiliser RxJS dans un composant React ?

Installez la bibliothèque dans votre projet. Le paquet rxjs-compat, que l’on voyait dans les anciens tutoriels pour migrer depuis RxJS 5, n’est plus publié depuis la version 7 et n’est plus nécessaire.
npm install rxjs
Voici un composant de recherche qui interroge l’API publique SWAPI (Star Wars API) pendant la saisie. Il remplace l’ancien modèle à base de classe et de componentWillUnmount par des hooks :
import { useEffect, useMemo, useState } from 'react';
import { Subject, debounceTime, distinctUntilChanged, filter, map, switchMap, catchError, of } from 'rxjs';
import { fromFetch } from 'rxjs/fetch';
// Transforme une requête fetch en Observable qui émet le JSON de la réponse
const lireJson = (url) =>
fromFetch(url).pipe(
switchMap((reponse) => {
if (!reponse.ok) throw new Error(`Erreur ${reponse.status}`);
return reponse.json();
}),
);
export function RecherchePersonnages() {
const [etat, setEtat] = useState({ resultats: [], erreur: null });
const saisie$ = useMemo(() => new Subject(), []);
useEffect(() => {
const abonnement = saisie$
.pipe(
map((texte) => texte.trim()),
debounceTime(400), // attend 400 ms sans frappe
distinctUntilChanged(), // ignore une valeur identique à la précédente
filter((texte) => texte.length >= 2),
switchMap((texte) => // annule la requête précédente
lireJson(`https://swapi.dev/api/people/?search=${encodeURIComponent(texte)}`).pipe(
map((page) => ({ resultats: page.results, erreur: null })),
catchError(() => of({ resultats: [], erreur: 'La recherche a échoué.' })),
),
),
)
.subscribe(setEtat);
return () => abonnement.unsubscribe(); // évite les fuites de mémoire
}, [saisie$]);
return (
<div>
<input type="search" onChange={(e) => saisie$.next(e.target.value)} placeholder="Rechercher un personnage" />
{etat.erreur && <p>{etat.erreur}</p>}
<ul>
{etat.resultats.map((perso) => <li key={perso.url}>{perso.name}</li>)}
</ul>
</div>
);
}
Ce qu’il faut comprendre :
- Le Subject sert d’entrée : chaque frappe y est poussée avec
next(). C’est une façon simple de créer un Observable à partir d’événements React. debounceTime(400)limite les appels à l’API : la requête ne part qu’après 400 ms sans nouvelle frappe.switchMappasse au nouvel Observable (la nouvelle requête) et se désabonne du précédent : une réponse lente à une ancienne saisie ne peut pas écraser les résultats récents.fromFetchannule même la requête HTTP en cours.catchError, placé à l’intérieur duswitchMap, transforme une erreur en valeur : le flux principal survit et la recherche suivante fonctionne encore.- Le nettoyage retourné par
useEffectdésabonne le flux quand le composant disparaît, pour éviter les fuites de mémoire et les mises à jour d’un composant démonté.
Depuis RxJS 7.2, les opérateurs s’importent directement depuis 'rxjs' ; l’import depuis 'rxjs/operators' est déprécié.
Comment combiner plusieurs requêtes ?
Autre cas fréquent : une API renvoie des objets dont certaines propriétés ne sont que des liens vers d’autres ressources. Dans SWAPI, la planète d’origine d’un personnage (homeworld) est une URL. Avec RxJS, on peut tout récupérer dans un seul flux :
import { map, mergeMap, forkJoin, tap, catchError, of } from 'rxjs';
const personnagesAvecPlanete$ = lireJson('https://swapi.dev/api/people/').pipe(
map((page) => page.results),
mergeMap((personnages) =>
forkJoin(
personnages.map((perso) =>
lireJson(perso.homeworld).pipe(
map((planete) => ({ ...perso, homeworld: planete.name })),
),
),
),
),
tap((liste) => console.log(`${liste.length} personnages chargés`)),
catchError((erreur) => {
console.error(erreur);
return of([]);
}),
);
forkJoinfonctionne commePromise.all: il attend que tous les Observables internes aient terminé, puis émet un tableau de leurs dernières valeurs. Idéal pour plusieurs requêtes HTTP en parallèle.mergeMap« aplatit » l’Observable produit parforkJoindans le flux principal. Les anciens exemples utilisentflatMap, qui n’est qu’un alias déprécié demergeMap, annoncé comme supprimé à la version majeure suivante.tapsert aux effets de bord (journalisation, envoi vers un store) sans modifier les valeurs.catchErrorintercepte l’erreur et la remplace par une valeur de repli.
Ce type d’orchestration est fréquent dans les applications web sur mesure qui agrègent plusieurs API. Pour situer React par rapport aux autres frameworks, voir notre comparatif Angular, React ou Vue.
Que propose la bibliothèque React-RxJS ?
Écrire à la main un useEffect avec abonnement et désabonnement pour chaque flux devient répétitif. React-RxJS est une bibliothèque qui fournit des liaisons entre React et RxJS : elle transforme un Observable en hook, gère le partage de l’état entre composants et prend en charge React Suspense. Elle s’installe avec npm install rxjs @react-rxjs/core @react-rxjs/utils.
import { bind, Subscribe } from '@react-rxjs/core';
import { createSignal } from '@react-rxjs/utils';
import { map } from 'rxjs';
const [texteModifie$, setTexte] = createSignal();
const [useTexte, texte$] = bind(texteModifie$, '');
const [useNbCaracteres] = bind(texte$.pipe(map((texte) => texte.length)));
function Champ() {
const texte = useTexte();
return <input value={texte} onChange={(e) => setTexte(e.target.value)} />;
}
function Compteur() {
return <p>{useNbCaracteres()} caractères</p>;
}
export function Formulaire() {
return (
<Subscribe>
<Champ />
<Compteur />
</Subscribe>
);
}
bind renvoie un hook et l’Observable partagé correspondant ; createSignal crée un point d’entrée pour les événements. La bibliothèque est restée en version 0.x (0.10.8 au moment où nous écrivons) : vérifiez son activité avant de l’adopter sur un projet à long terme. Sans dépendance supplémentaire, React propose aussi useSyncExternalStore pour s’abonner proprement à une source externe, ce qui peut servir à brancher un Observable.
RxJS ou MobX ?
Les deux ne répondent pas au même besoin. MobX est un gestionnaire d’état : on déclare des données observables, et l’interface se met à jour quand elles changent. RxJS est une bibliothèque de gestion d’événements asynchrones.
- Utilisez RxJS dès que le temps entre en jeu (délais, temporisation, annulation) ou que vous devez raisonner sur l’historique des valeurs d’un flux.
- Utilisez MobX (ou un autre gestionnaire d’état) pour réagir à un état plutôt qu’à des événements : c’est une approche plus simple et de plus haut niveau.
Les deux se combinent bien : RxJS traite et limite les événements utilisateur, puis met à jour un état MobX, qui se charge de rafraîchir l’interface. Dans l’autre sens, la fonction toStream du paquet mobx-utils convertit une expression MobX en flux observable compatible avec RxJS.
Comment tester un flux RxJS ?

Le conseil le plus utile : sortez la logique des composants. Un pipeline écrit comme une fonction qui reçoit un Observable et en renvoie un autre se teste isolément, sans React :
import { of, map } from 'rxjs';
export const ajouterSuffixe = (source$) => source$.pipe(map((texte) => texte + ' search'));
it('ajoute le suffixe', (done) => {
ajouterSuffixe(of('text')).subscribe((valeur) => {
expect(valeur).toBe('text search');
done();
});
});
Pour tout ce qui dépend du temps (debounceTime, delay, interval), RxJS fournit le TestScheduler et les « marble tests » : on décrit les flux sous forme de diagrammes textuels, et le temps est simulé, donc le test s’exécute instantanément. Ici, trois frappes espacées de 100 ms et une temporisation de 300 ms : seule la dernière valeur sort, 300 ms après la dernière frappe.
import { TestScheduler } from 'rxjs/testing';
import { debounceTime } from 'rxjs';
const testScheduler = new TestScheduler((obtenu, attendu) => {
expect(obtenu).toEqual(attendu);
});
it('ne garde que la dernière frappe', () => {
testScheduler.run(({ cold, expectObservable }) => {
const saisie$ = cold('a 100ms b 100ms c');
expectObservable(saisie$.pipe(debounceTime(300))).toBe('502ms c');
});
});
Côté composant, on ne teste pas l’abonnement lui-même mais son effet visible : avec React Testing Library et les minuteurs simulés de votre outil de test, on saisit plusieurs caractères et on vérifie qu’une seule requête est partie. Pour organiser l’ensemble, voir notre article sur les tests automatisés en développement web.
Où en est RxJS aujourd’hui ?
- Version stable : RxJS 7, dont la dernière version, la 7.8.2, a été publiée en février 2025.
- Version suivante : la version 8 n’a jamais dépassé le stade alpha (dernière alpha en janvier 2024) ; une version 9 est publiée en bêta depuis août 2026. Attendez sa sortie stable et son guide de migration avant de l’utiliser en production.
- Angular reste le principal utilisateur de RxJS : son client HTTP renvoie des Observables, et le paquet
@angular/core/rxjs-interopfait le pont avec les signals du framework (toSignal). - Un Observable natif dans le navigateur : une API Observable, proposée au WICG (le groupe d’incubation du W3C), est disponible dans Chrome depuis la version 135. Elle reste marquée expérimentale et n’est pas prise en charge par Firefox ni Safari. RxJS reste donc la solution portable.
En résumé, RxJS est un excellent outil entre les mains de développeurs qui le maîtrisent, et il s’intègre facilement à React. Son inconvénient est le coût d’entrée pour une équipe qui n’a jamais travaillé en programmation réactive. Si vous hésitez sur l’architecture d’une application React ou sur l’intérêt de RxJS pour votre projet, écrivez-nous à contact@osmova.com ou via le formulaire de contact.
Questions fréquentes
RxJS est-il réservé à Angular ?
Non. Angular l’utilise en interne, mais RxJS est une bibliothèque indépendante qui fonctionne avec React, Vue, Node.js ou du JavaScript sans framework.
Faut-il RxJS pour faire des appels d’API dans React ?
Non. Pour un appel simple, fetch avec async/await suffit. RxJS devient intéressant quand il faut temporiser, annuler, enchaîner ou combiner plusieurs sources.
Quelle différence entre switchMap, mergeMap et concatMap ?
Les trois transforment chaque valeur en un nouvel Observable. switchMap abandonne le précédent quand une nouvelle valeur arrive (recherche), mergeMap les exécute tous en parallèle, concatMap les exécute l’un après l’autre dans l’ordre (enregistrements successifs).
Pourquoi faut-il se désabonner ?
Un Observable qui ne se termine pas (événements, intervalle, WebSocket) continue d’émettre tant qu’on y est abonné. Sans unsubscribe() au démontage du composant, on crée des fuites de mémoire et des mises à jour inutiles.
Quelle différence entre Subject, BehaviorSubject et ReplaySubject ?
Un Subject transmet les nouvelles valeurs à ses abonnés du moment : un abonné arrivé en retard ne reçoit rien de ce qui a été émis avant. Un BehaviorSubject se crée avec une valeur initiale et envoie immédiatement la valeur courante à chaque nouvel abonné, ce qui en fait un petit conteneur d’état. Un ReplaySubject rejoue aux nouveaux abonnés un nombre donné de valeurs passées.
Quelle ressource pour apprendre RxJS ?
La documentation officielle sur rxjs.dev, très complète, avec un arbre de décision pour choisir un opérateur. Le site communautaire Learn RxJS est pratique pour consulter rapidement des exemples par opérateur.
Sources
- RxJS : présentation officielle
- npm : paquet rxjs (versions publiées)
- GitHub ReactiveX/rxjs : versions et notes de publication
- RxJS : changements majeurs de la version 7 (rxjs-compat)
- RxJS : dépréciation de toPromise, firstValueFrom et lastValueFrom
- RxJS : import des opérateurs depuis ‘rxjs’ (7.2)
- RxJS : arbre de décision des opérateurs
- RxJS : tests avec TestScheduler et diagrammes marble
- React-RxJS : démarrage
- React : useSyncExternalStore
- mobx-utils : toStream
- Angular : interopérabilité RxJS et signals
- WICG : spécification de l’API Observable
- MDN browser-compat-data : compatibilité de l’API Observable
- SWAPI : API utilisée dans les exemples





