Go to blue arrow
back to Tech Blog
Développement
André Santos
Alexandra Mendes

11 août 2026

Min Read

Comment gérer les opérations asynchrones avec Redux

MacBook Pro affichant du code et une sortie terminal pour les opérations async avec Redux

La logique asynchrone n'a pas sa place dans un réducteur Redux. Les réducteurs étant des fonctions pures, chaque appel API, chaque minuteur et chaque abonnement doit être géré ailleurs, et dans Redux cet « ailleurs » correspond au middleware. Redux Thunk est le point de départ par défaut. Pour la plupart des équipes, c'est aussi le point d'arrivée.

Ce qui suit est tiré d'une vaste application React et Node.js que nous avons migrée depuis une couche d'état ad hoc. La première partie traite de la décision et de ses coûts ; à partir de « Comment implémenter Redux Thunk », il s'agit de mise en œuvre. Si seule la décision vous intéresse, arrêtez-vous aux critères de sélection.

blue arrow to the left
Imaginary Cloud logo

Pourquoi utilisons-nous Redux ?

Lorsque nous avons dû définir une pile technologique pour ce projet, React s'est imposé comme une évidence pour le front-end, compte tenu de l'importance de la présentation visuelle et de la modélisation 3D.

Ce que nous n'avions pas défini, c'était une stratégie de gestion d'état. À mesure que le projet évoluait, cela est devenu problématique. L'équipe a donc déplacé toute la logique de gestion d'état dans une classe implémentée sous forme de Singleton : une instance unique partagée dans laquelle chaque composant peut lire et écrire. Cela gérait assez bien le stockage et les événements liés à l'état.

Assez bien, pendant un temps. Mais la solution a fini par atteindre ses limites, et nous avons mis en place un plan pour trouver une meilleure alternative. Elle est arrivée sous la forme de Redux, appuyée par l'introduction de Redux Toolkit, anciennement connu sous le nom de Redux Starter Kit.

blue arrow to the left
Imaginary Cloud logo

Ce que la couche d'état ad hoc nous a réellement coûté

Le Singleton n'a jamais été prévu. Personne ne choisit cette solution. C'est le point essentiel à retenir de cet article, car un état non géré est rarement un problème technique à l'origine. Il se manifeste par un coût de livraison.

Imaginez un tableau blanc dans un bureau partagé sur lequel tout le monde peut écrire, sans noms ni horodatage. Le tableau vous indique toujours l'état actuel des choses. Il ne vous dit jamais qui l'a modifié, ni quand, ni pourquoi. Pour comprendre comment une valeur est arrivée là, il faut donc interroger chaque personne qui est passée par là.

Schéma d'architecture sur tableau blanc comparant le pattern Singleton et le flux d'état pour la gestion asynchrone avec Redux.

C'était notre Singleton, et son coût s'est fait sentir dans trois domaines quantifiables. Le débogage prenait plus de temps, car retracer une valeur erronée signifiait lire manuellement chaque contributeur. [Auteur à confirmer : combien d'heures de développement par sprint l'équipe consacrait à ce traçage avant la migration.] L'intégration prenait plus de temps, car l'instance partagée n'avait aucun contrat qu'un nouveau développeur pouvait consulter ; la seule façon d'apprendre le modèle d'état était donc de lire chaque composant qui y touchait. [Auteur à confirmer : combien de temps il fallait à un nouveau développeur pour devenir opérationnel sur la couche d'état, avant et après.] Et chaque nouvelle fonctionnalité entraînait une petite taxe, car quelqu'un devait déterminer manuellement quelles parties de l'objet partagé pouvaient être modifiées sans risque.

Redux n'a pas rendu l'application plus rapide. Il a rendu la couche d'état lisible. Les actions sont nommées, les mutations sont enregistrées et l'historique est consultable pendant les tests, de sorte que le temps que nous perdions à nous demander « d'où vient cette valeur » a largement disparu. Si vous envisagez une migration comme celle-ci, c'est le retour sur investissement à mesurer : non pas les performances à l'exécution, mais les heures que votre équipe passe actuellement à reconstruire l'état manuellement. C'est le même retour que nous recherchons lorsque nous effectuons un audit de code sur une base de code existante.

blue arrow to the left
Imaginary Cloud logo

Qu'est-ce qu'un store Redux ?

Redux est un conteneur d'état pour les applications JavaScript qui permet de contourner le flux de données unidirectionnel naturel de React. Il constitue une source unique de vérité que vous pouvez consulter n'importe où dans votre application, sans avoir à transmettre l'état via des props à d'autres composants.

Il permet également de modifier cet état via des actions prédéfinies, tout en conservant un historique de ces actions et mutations consultable lors des tests. Des rédacteurs nommés, horodatés. Le tableau blanc, enfin, accompagné d'un journal de bord.

Qui a créé Redux ?

Lorsque Facebook a présenté React au monde, il était déjà évident que les props pouvaient devenir un obstacle majeur à un certain niveau de complexité. Pour atténuer cela, ils ont introduit un concept aux côtés de React appelé Flux, qui décrit comment un store doit fonctionner en conjonction avec React. Redux, tel que nous le connaissons aujourd'hui, découle d'une preuve de concept que Dan Abramov a construite en expérimentant les principes de Flux pour React Europe.

Où Redux est-il utilisé ?

Redux est utilisé dans les applications à grande échelle où l'interaction avec un composant peut propager des changements à l'ensemble de la page. Plutôt que de créer des rappels au niveau supérieur de l'application et de les transmettre, il suffit de consulter le store.

Dans mon projet, c'était logique car l'application avait atteint un point où les props liées à l'état étaient transmises à travers plusieurs couches de composants. Cela rendait le code difficile à lire et encore plus difficile à déboguer. (Le même raisonnement s'applique au mobile. Si vous intégrez Redux dans React Native, nous détaillons la configuration dans notre guide React Native avec Redux.)

blue arrow to the left
Imaginary Cloud logo

Déclencher des actions asynchrones avec Redux

Redux a immédiatement fait ses preuves lorsque nous avons commencé à y migrer notre ancien code. Le code est devenu plus facile à suivre, l'équipe l'a pris en main rapidement et le nombre de props circulant dans l'application a chuté drastiquement.

Mais tout n'était pas parfait. La nature même des reducers pose problème dès lors que l'on tente d'y intégrer la récupération de données, un défi sur lequel la communauté Redux travaille depuis très longtemps.

Comment gérer les actions asynchrones dans Redux

En théorie, les reducers sont des fonctions pures, selon la documentation elle-même.

À partir des mêmes arguments, ils doivent calculer l'état suivant et le renvoyer. Aucune surprise. Aucun effet de bord. Aucun appel API. Aucune mutation. Juste un calcul.

Alors, où placer les appels asynchrones dans Redux ?

Les actions devraient être la réponse immédiate, mais l'implémentation de base d'une action n'est rien de plus qu'un simple objet JavaScript utilisé pour transmettre des informations à votre store. La communauté a donc imaginé les middlewares : des fonctions qui s'intercalent entre le déclenchement d'une action et sa réception par le reducer, tel un service de tri entre la boîte aux lettres et le classeur. Le courrier arrive, quelqu'un l'ouvre, effectue les démarches nécessaires, et ce n'est qu'ensuite que le tout est classé. Ils encapsulent la logique dans des fonctions et imitent le comportement naturel du store.

Quel middleware Redux asynchrone choisir ?

Comme pour tout en programmation, il n'existe pas de solution universelle ; recherchez donc le middleware le mieux adapté à votre problème. La première solution suggérée par la documentation est Redux Thunk. Un thunk est une fonction qui en renvoie une autre, différant ainsi l'exécution jusqu'à ce qu'elle soit appelée, ce qui, dans Redux, se fait via dispatch. Ce middleware vous permet de créer des actions qui ne sont plus de simples objets : elles peuvent déclencher d'autres actions, appeler d'autres Thunks et effectuer des opérations asynchrones en leur sein.

D'autres solutions ont gagné en popularité depuis. Redux-Saga modélise les flux asynchrones sous forme de sagas, qui sont des fonctions génératrices : des fonctions qui se mettent en pause à chaque étape et rendent le contrôle à un moteur qui décide de la suite des opérations. Redux-Observable modélise ces mêmes flux sous forme de flux RxJS, où chaque action est un événement sur un flux que vous pouvez filtrer, combiner et annuler à l'aide des opérateurs de la bibliothèque RxJS. Des cas d'usage différents, et une mise en garde importante avant de vous lancer : Saga est toujours activement maintenu par une large communauté, tandis que Redux-Observable est stable mais désormais en mode maintenance. Pesez donc le pour et le contre en fonction de la pérennité de votre base de code.

Une quatrième option existe, complémentaire plutôt que directement concurrente. RTK Query est la couche de récupération de données de Redux Toolkit : vous déclarez vos points de terminaison d'API et elle génère automatiquement la logique de récupération, de mise en cache et de chargement, vous évitant ainsi d'avoir à écrire manuellement la moindre action asynchrone.

Bannière « Do a UX Audit » : smartphone bleu avec fenêtres de design superposées et bouton « Talk to Us ».
blue arrow to the left
Imaginary Cloud logo

Critères de sélection du middleware Imaginary Cloud

« Faites vos recherches » n'est pas un conseil. Pour nos projets clients, nous choisissons parmi quatre options en nous basant sur trois critères, dans cet ordre.

  1. Quelle est la complexité du flux asynchrone lui-même ? Une requête qui démarre, réussit ou échoue est un Thunk. Un flux qui doit être annulé, débouncé (c'est-à-dire regroupé en un seul appel lors d'un afflux massif), réessayé selon un calendrier ou coordonné avec d'autres requêtes en cours est là où Saga et Observable commencent à justifier leur coût.
  2. S'agit-il d'un état serveur ou d'un état client ? La majeure partie de ce que les équipes placent dans un store Redux est un cache de données appartenant à un serveur. Si c'est ce que vous gérez, RTK Query est l'outil approprié, et il supprime entièrement le code de récupération. Les Thunks sont destinés aux états dont votre client est réellement propriétaire.
  3. Que l'équipe peut-elle maintenir dans un an ? Sagas et Observables ajoutent tous deux un modèle de programmation — générateurs ou flux réactifs — que chaque futur mainteneur doit apprendre. Dans une équipe qui ne maîtrise pas déjà RxJS, ce coût s'applique à chaque nouvelle recrue, et non une seule fois au démarrage.

En pratique, cela se résume à Thunk ou RTK Query sur la plupart de nos projets, et les deux coexistent confortablement dans le même store. [Auteur à confirmer : combien de nos récents projets React ont abouti à chaque option, sur quelle période, et un projet où le premier critère était réellement complexe et a mené à Saga ou Observable.] Si votre réponse au premier critère est réellement complexe, ayez cette discussion avant d'écrire le code, pas après.

Le coût de cette décision en termes de livraison

Pour quiconque valide le travail plutôt que de l'écrire, ces trois critères se traduisent par des éléments quantifiables dans un planning. Adopter Thunk sur une base de code Redux existante représente quelques jours de travail, car il s'agit de JavaScript asynchrone ordinaire que votre équipe maîtrise déjà. Adopter Saga ou Observable représente des semaines, et le coût est récurrent : chaque développeur que vous recrutez sur cette base de code doit apprendre les générateurs ou RxJS avant de pouvoir toucher à un flux de données, ce qui allonge l'intégration pendant toute la durée de vie du projet.

Revenir sur ce choix est la partie coûteuse. Après un an, la logique asynchrone écrite sous forme de sagas est dispersée dans toute la base de code, et la défaire relève de la migration plutôt que du refactoring, à une échelle comparable au passage de Singleton à Redux décrit plus haut.

Cette asymétrie constitue tout l'argument en faveur du choix de l'option la plus simple répondant au besoin dès le départ. Passer de Thunk à une solution plus complexe plus tard est additif, car les deux peuvent fonctionner côte à côte dans le même store. L'inverse n'est pas vrai.

blue arrow to the left
Imaginary Cloud logo

Pourquoi Redux Thunk ?

Parmi toutes les solutions populaires à ce problème, Redux Thunk est la plus simple à comprendre. Elle est techniquement assez accessible et, au moment où nous écrivons ces lignes, c'est l'approche recommandée par la documentation de Redux pour la récupération manuelle de données.

blue arrow to the left
Imaginary Cloud logo

Comment implémenter Redux Thunk

La procédure ci-dessous utilise une application vierge et une API publique dédiée aux chiens, afin que le code reste suffisamment concis pour être lu d'une traite. L'essentiel n'est toutefois pas dans les étapes elles-mêmes, mais dans les deux décisions qui les sous-tendent : ce qui doit figurer dans le store par rapport à ce qui doit rester dans le composant, et la raison d'être de la version longue.

Commençons par ceci :

npx create-react-app doggos --template redux

Cela vous permet d'obtenir une nouvelle application React intégrant tous les modules Redux nécessaires à ce court tutoriel. Nous utiliserons également le service API WoofBot.

Configuration d'un slice Redux Toolkit pour la réponse de l'API

Commençons par le choix stratégique. Seules les données sur les races et le statut de la requête doivent être stockés, car plusieurs parties de l'application en ont besoin. Tout ce qui concerne un composant unique lorsqu'il est affiché, comme la race sélectionnée dans une liste déroulante, doit rester dans ce composant. Si vous ignorez cette règle, votre store deviendra un fourre-tout, ce qui engendrera précisément le type de problèmes de débogage que Redux est censé résoudre.

Un slice est un ensemble Redux Toolkit qui regroupe une section du store ainsi que les reducers et les actions qui la modifient. Celui-ci centralise tout ce qui concerne la réponse de votre API canine.

// doggosSlice.js
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  breeds: [],
  images: {},
  loading: 'waiting',
};

export const doggosSlice = createSlice({
  name: 'doggos',
  initialState,
  reducers: {
    uploadBreeds: (state, action) => {
      state.breeds = action.payload;
    },
    uploadBreedImage: (state, action) => {
      state.images[action.payload.breed] = action.payload.image;
    },
    loadingState: (state, action) => {
      state.loading = action.payload;
    },
  },
});

export const { uploadBreeds, uploadBreedImage, loadingState } = doggosSlice.actions;

export const selectBreeds = (state) => state.doggos.breeds;
export const selectBreedImage = (breed) => (state) => state.doggos.images[breed];
export const isLoading = (state) => state.doggos.loading === 'request';

export default doggosSlice.reducer;

Voici nos actions :

  • uploadBreeds: sera utilisé pour stocker l'ensemble des informations relatives aux races de chiens.
  • uploadBreedImage: sera utilisé pour charger des images spécifiques à certaines races, si nécessaire.
  • loadingState: sera utilisé pour mettre à jour le statut de la requête.

Et nos sélecteurs, ces fonctions qui lisent une valeur dans le store afin que les composants n'accèdent jamais directement à sa structure :

  • selectBreeds: renvoie un tableau contenant toutes les races présentes dans le store.
  • selectBreedImage: renvoie l'image correspondant à une race spécifique.
  • isLoading: renvoie le statut de la requête.

Récupération de données dans un hook useEffect sans Redux Thunk

Comment implémenteriez-vous normalement ces échanges entre l'API et le store ? Je placerais le tout dans un hook useEffect, similaire à celui-ci :

useEffect(() => {
  dispatch(loadingState('request'));

  fetch('https://dog.ceo/api/breeds/list/all')
    .then((response) => response.json())
    .then((data) => {
      dispatch(uploadBreeds(Object.keys(data.message)));
      dispatch(loadingState('waiting'));
    })
    .catch(() => {
      dispatch(loadingState('error'));
    });
}, [dispatch]);

Que faisons-nous ici ?

  1. Le composant est monté.
  2. L'état de chargement est défini sur « Request » via un dispatch d'action.
  3. Les données sont demandées à l'aide d'un simple fetch.
  4. Les données sont reçues de l'API et traitées pour obtenir l'objet souhaité.
  5. Les informations sur la race dans le slice sont mises à jour avec les données reçues, via un autre dispatch.
  6. L'état de chargement est réinitialisé sur « Waiting ».

Alternativement, nous pourrions recevoir une erreur de l'API, ce qui interrompt le flux à l'étape 4 et définit l'état de chargement sur « Error ».

Cela fonctionne, et c'est très bien. Cependant, cette approche présente plusieurs inconvénients. Principalement, elle surcharge le composant en logique : elle n'est pas réutilisable, et si vous avez besoin de ces informations ailleurs, vous devrez toujours vous assurer que ce composant a été chargé au préalable.

Déplacer la même récupération de données dans un Redux Thunk

La logique du composant ressemble à ceci :

useEffect(() => {
  dispatch(fetchBreeds());
}, [dispatch]);

Nous devons créer une nouvelle action fetchBreeds qui ressemble beaucoup à la logique que nous avions précédemment dans le composant :

export const fetchBreeds = () => async (dispatch) => {
  dispatch(loadingState('request'));

  try {
    const response = await fetch('https://dog.ceo/api/breeds/list/all');
    const data = await response.json();

    dispatch(uploadBreeds(Object.keys(data.message)));
    dispatch(loadingState('waiting'));
  } catch (error) {
    dispatch(loadingState('error'));
  }
};

Ce simple changement d'emplacement résout la plupart des problèmes rencontrés. Nous avons extrait le code du composant et rendu cette logique spécifique réutilisable dans toute la base de code. Les informations ne sont plus liées au montage du composant ; vous pouvez donc déclencher une nouvelle action fetchBreeds n'importe où pour charger les données.

export const fetchBreedImages = () => async (dispatch, getState) => {
  const breeds = selectBreeds(getState());

  for (const breed of breeds) {
    const response = await fetch(`https://dog.ceo/api/breed/${breed}/images/random`);
    const data = await response.json();

    dispatch(uploadBreedImage({ breed, image: data.message }));
  }
};

Cela nous permet également de chaîner les Thunks, en en déclenchant un depuis un autre lorsque la logique de nos actions devient plus complexe. Nous pouvons accéder directement à l'état via getState, sans avoir besoin de sélecteurs. Vous voudrez toutefois conserver les sélecteurs pour éviter que toute modification de la structure de votre état Redux ne casse vos Thunks.

Utiliser createAsyncThunk plutôt que d'écrire un thunk manuellement

Tout ce qui précède est écrit de manière détaillée à des fins pédagogiques. Cela montre ce qu'est réellement un Thunk : une fonction que vous déclenchez, qui reçoit dispatch et getState en paramètres. Dans un projet utilisant Redux Toolkit, vous ne l'écririez pas de cette façon. createAsyncThunk génère pour vous les actions en attente, réussies et rejetées, de sorte que l'état de chargement que vous venez de nous voir déclencher trois fois soit géré une seule fois par le réducteur.

import { createAsyncThunk } from '@reduxjs/toolkit';

export const fetchBreeds = createAsyncThunk('doggos/fetchBreeds', async () => {
  const response = await fetch('https://dog.ceo/api/breeds/list/all');
  const data = await response.json();
  return Object.keys(data.message);
});

extraReducers: (builder) => {
  builder
    .addCase(fetchBreeds.pending, (state) => {
      state.loading = 'request';
    })
    .addCase(fetchBreeds.fulfilled, (state, action) => {
      state.breeds = action.payload;
      state.loading = 'waiting';
    })
    .addCase(fetchBreeds.rejected, (state, action) => {
      state.loading = 'error';
      state.error = action.error.message;
    });
}

Gérer les erreurs et l'annulation dans un Redux Thunk

Deux points que les tutoriels omettent généralement. Les deux mêmes points qui causent des problèmes en production.

Erreurs. Un Thunk rejeté doit placer un message dans le store, et pas seulement un statut. action.error.message ci-dessus est le minimum. Sur des projets réels, nous conservons la charge utile d'erreur de la requête ayant échoué, en utilisant rejectWithValue, afin que le composant puisse distinguer une erreur 404 d'une défaillance réseau et afficher un message utile à l'utilisateur.

Annulation. La boucle fetchBreedImages ci-dessus continuera de s'exécuter après le démontage du composant, et une réponse lente arrivant après une réponse plus récente l'écrasera. createAsyncThunk vous fournit un signal que vous pouvez transmettre à fetch pour résoudre le premier problème, ainsi qu'une option condition pour empêcher le lancement d'une requête en double pour le second. Besoin de plus ? L'annulation est précisément le critère qui justifie l'utilisation de Saga ou d'Observable.

Comment tester un thunk et un createAsyncThunk

Les Thunks sont testables précisément parce qu'il s'agit de fonctions simples, et c'est la raison pratique pour laquelle il faut les préférer à une logique enfouie dans un composant. Aucun composant à rendre. Aucun hook à simuler.

Pour un Thunk écrit manuellement, appelez-le avec un faux dispatch et un faux getState, puis vérifiez ce qui a été déclenché et dans quel ordre :

it('dispatches request, then breeds, then waiting', async () => {
  const dispatch = jest.fn();
  global.fetch = jest.fn().mockResolvedValue({
    json: () => Promise.resolve({ message: { husky: [], beagle: [] } }),
  });

  await fetchBreeds()(dispatch, () => ({}));

  expect(dispatch.mock.calls.map(([action]) => action)).toEqual([
    loadingState('request'),
    uploadBreeds(['husky', 'beagle']),
    loadingState('waiting'),
  ]);
});

Pour un createAsyncThunk, il y a deux éléments distincts à tester, et il est préférable de les séparer. Le thunk lui-même est testé en le déclenchant sur un store réel et en vérifiant l'état résultant, ce qui permet de tester de bout en bout les chemins pending et fulfilled. Le réducteur est testé en tant que fonction pure, en l'appelant avec une action fetchBreeds.rejected et en vérifiant que l'erreur est bien enregistrée là où vous l'attendez :

it('records the error message when the request is rejected', () => {
  const action = { type: fetchBreeds.rejected.type, error: { message: 'Network error' } };
  const state = doggosReducer(initialState, action);

  expect(state.loading).toBe('error');
  expect(state.error).toBe('Network error');
});

Le chemin rejeté est celui que les équipes oublient de tester. C'est pourtant celui que les utilisateurs voient.

Comment les thunks et les sélecteurs interagissent à mesure que le store grandit

Un Thunk qui appelle getState lit l'intégralité du store, et c'est un couplage qu'il vaut mieux gérer rapidement. Lisez via un sélecteur, comme le fait fetchBreedImages, et le Thunk dépendra du contrat du sélecteur plutôt que de la structure de votre arbre d'état. Si vous modifiez la structure de la tranche plus tard, vous n'aurez qu'à mettre à jour le sélecteur une seule fois, au lieu de devoir traquer chaque Thunk qui y accédait.

Le deuxième problème concerne la taille. Un sélecteur qui dérive une valeur, filtre des races ou construit une table de correspondance s'exécute à chaque modification du store et renvoie un nouvel objet à chaque fois, ce qui provoque le re-rendu des composants même lorsque les données affichées n'ont pas changé. Redux Toolkit propose createSelector pour cela : il mémorise le résultat, ce qui signifie qu'il met en cache la dernière sortie et ne recalcule que lorsque les entrées changent réellement.

import { createSelector } from '@reduxjs/toolkit';

export const selectBreedsWithImages = createSelector(
  [selectBreeds, (state) => state.doggos.images],
  (breeds, images) => breeds.filter((breed) => images[breed]),
);

Des sélecteurs simples pour les lectures directes, des sélecteurs mémorisés pour tout ce qui est dérivé. Sur un petit store, la différence est invisible. Sur le store final de ce projet, c'est la différence entre une page réactive et une page qui saccade.

Quand RTK Query remplace entièrement Redux Thunk

Si l'état que vous récupérez appartient à un serveur plutôt qu'à votre client, RTK Query supprime entièrement le code ci-dessus. Vous déclarez le point de terminaison, et le hook généré gère la requête, la mise en cache, les indicateurs de chargement, la déduplication des requêtes simultanées — ce qui signifie que deux composants demandant les mêmes données en même temps ne produisent qu'un seul appel réseau au lieu de deux — ainsi que l'invalidation lors de l'écriture, ce qui signifie qu'une mise à jour réussie marque automatiquement les données en cache concernées comme obsolètes et les récupère à nouveau.

Aucun Thunk à écrire. Aucun état de chargement à envoyer. La documentation de Redux enseigne désormais RTK Query comme l'approche par défaut pour la récupération de données, et comme les races de chiens dans cet article constituent un état serveur, c'est vers cette solution que nous nous tournerions en priorité sur un projet réel.

blue arrow to the left
Imaginary Cloud logo

Au final, quelle solution choisir pour gérer les opérations asynchrones dans Redux ?

Voici l'argumentaire en résumé. Les réducteurs doivent rester purs, le travail asynchrone a donc sa place dans les middlewares. Redux Thunk est le choix par défaut idéal, car il n'ajoute aucun nouveau modèle de programmation et couvre les cas de succès ou d'échec des requêtes qui constituent la majorité des applications ; c'est avec createAsyncThunk que vous devriez l'implémenter. Si les données proviennent d'un serveur, utilisez plutôt RTK Query et oubliez les Thunks. Ne vous tournez vers Saga ou Observable que si vos flux nécessitent une annulation, une coordination ou une planification que les deux premières options ne permettent pas, et seulement si votre équipe sera toujours en mesure de maintenir ce modèle dans un an.

Pour les problèmes que j'ai rencontrés récemment, les Thunks ont largement suffi à couvrir tous les cas particuliers. Commencez par là. Laissez ensuite un besoin concret, et non une simple préférence, justifier une éventuelle migration.

blue arrow to the left
Imaginary Cloud logo

Foire aux questions

Est-il possible d'effectuer des appels asynchrones dans un réducteur Redux ?

Non. Les réducteurs doivent être des fonctions pures : à arguments identiques, ils doivent retourner le même état suivant, sans effets de bord ni appels API. Le travail asynchrone doit être géré dans un middleware, qui s'intercale entre l'envoi d'une action et sa réception par le réducteur. Redux Thunk est le middleware standard pour cela.

Quelle est la différence entre Redux Thunk et Redux-Saga ?

Un Thunk est une fonction simple qui reçoit dispatch et getState ; il s'agit donc simplement du JavaScript asynchrone que vous connaissez déjà. Une Saga est une fonction génératrice, et Redux-Saga est un moteur permettant d'exécuter ces générateurs, offrant ainsi l'annulation, le debouncing, les tentatives de réessai et la coordination entre flux concurrents comme fonctionnalités natives. Thunk est plus simple. Saga est plus puissant pour les flux complexes, mais demande un investissement plus important en apprentissage et en maintenance.

Dois-je utiliser createAsyncThunk ou écrire mon propre thunk ?

Utilisez createAsyncThunk. Il génère automatiquement les actions pending, fulfilled et rejected, normalise la structure des erreurs et offre une prise en charge de l'annulation via signal et condition. N'écrivez un Thunk manuellement que lorsqu'il n'effectue aucune requête, par exemple pour lire l'état et envoyer une action simple de manière conditionnelle.

Ai-je encore besoin de Redux en 2026 ?

Moins souvent que les équipes ne le pensent. Si l'état que vous gérez est un cache de données serveur, RTK Query ou une bibliothèque de récupération de données suffit. S'il est local à un petit arbre de composants, React context ou l'état du composant est généralement suffisant. Redux trouve sa place lorsqu'une grande quantité d'état appartenant au client est partagée entre des parties éloignées d'une application complexe et que vous avez besoin de la traçabilité des actions nommées ainsi qu'un historique inspectable.

Comment gérer les erreurs dans un thunk Redux ?

Capturez l'échec à l'intérieur du Thunk et envoyez-le dans le store en tant que donnée, et non simplement comme un indicateur de statut. Avec createAsyncThunk, retournez rejectWithValue(error) afin que l'action rejected transporte la charge utile d'erreur de l'API, puis gérez .rejected dans extraReducers. De cette façon, un composant peut distinguer une erreur de validation d'une erreur réseau et afficher un message spécifique à l'utilisateur.

Choisir une stratégie de gestion d'état pour votre base de code

La plupart des problèmes d'état que nous sommes appelés à résoudre ont commencé de la même manière que les nôtres. Aucune décision n'a été prise, et une solution ad hoc est devenue la couche d'état par défaut. Si cela ressemble à votre application, nos équipes peuvent évaluer ce que votre gestion d'état vous coûte réellement en temps de débogage et d'intégration avant de vous faire des recommandations. Discutez-en avec nous et découvrez notre approche de la livraison sur nos ingénierie logicielle et audit de code pages, ou parcourez nos études de cas, notamment notre travail sur React et Redux pour Elephants Don't Forget, pour voir le processus en action.

André Santos
André Santos

Votre développeur web de tous les jours qui aime se cacher dans le backend. Javascript et Ruby sont mes préférés. Je me débrouille toujours avec Docker et mes builds se cassent assez souvent.

Read more posts by this author
Alexandra Mendes
Alexandra Mendes

Alexandra Mendes est spécialiste senior de la croissance chez Imaginary Cloud et possède plus de 3 ans d'expérience dans la rédaction de textes sur le développement de logiciels, l'IA et la transformation numérique. Après avoir suivi un cours de développement frontend, Alexandra a acquis des compétences pratiques en matière de codage et travaille désormais en étroite collaboration avec les équipes techniques. Passionnée par la façon dont les nouvelles technologies façonnent les entreprises et la société, Alexandra aime transformer des sujets complexes en contenus clairs et utiles pour les décideurs.

LinkedIn

Read more posts by this author

People who read this post, also found these interesting:

Dropdown caret icon