45 votes

Comment éviter les conditions de course lors de la récupération de données avec Redux ?

Nous avons une action qui récupère un objet de manière asynchrone, appelons-la getPostDetails qui prend en paramètre le poste à récupérer par un identifiant. L'utilisateur se voit présenter une liste de messages et peut cliquer sur l'un d'eux pour obtenir des détails.

Si un utilisateur clique sur "Post #1", nous envoyons un GET_POST qui pourrait ressembler à quelque chose comme ceci.

const getPostDetails = (id) => ({
  type: c.GET_POST_DETAILS,
  promise: (http) => http.get(`http://example.com/posts/#${id}`),
  returnKey: 'facebookData'
})

Cette information est récupérée par un intergiciel, qui ajoute un gestionnaire de succès à la promesse, qui appellera une action telle que GET_POST__OK avec l'objet JSON désérialisé. Le reducer voit cet objet et l'applique à un magasin. Un exemple typique __OK Le réducteur ressemble à ceci.

[c.GET_ALL__OK]: (state, response) => assign(state, {
  currentPost: response.postDetails
})

Plus tard, nous avons un composant qui examine currentPost et affiche les détails du poste actuel.

Cependant, nous avons une condition de course. Si un utilisateur soumet deux GET_POST_DETAILS actions l'une après l'autre, il n'y a aucune pas de garantie sur l'ordre dans lequel nous recevons les __OK dans, si la deuxième requête http se termine avant la première, la deviendra incorrect.

    Action                   => Result
    ---------------------------------------------------------------------------------
|T| User Clicks Post #1      => GET_POST for #1 dispatched => Http Request #1 pending
|i| User Clicks Post #2      => GET_POST for #2 dispatched => Http Request #2 pending
|m| Http Request #2 Resolves => Results for #2 added to state
|e| Http Request #1 Resolves => Results for #1 added to state
 V

Comment faire en sorte que le dernier élément sur lequel l'utilisateur a cliqué soit toujours prioritaire ?

85voto

Dan Points 16670

Le problème est dû à une organisation sous-optimale de l'État.

Dans une application Redux, les clés d'état comme currentPost sont généralement un anti-modèle. Si vous devez "réinitialiser" l'état à chaque fois que vous naviguez vers une autre page, vous avez perdu l'un des avantages de l'utilisation de la fonction Principaux avantages de Redux (ou Flux) : mise en cache. Par exemple, il n'est plus possible de revenir instantanément en arrière si toute navigation réinitialise l'état et réinitialise les données.

Une meilleure façon de stocker ces informations serait de séparer les informations suivantes postsById y currentPostId :

{
  currentPostId: 1,
  postsById: {
    1: { ... },
    2: { ... },
    3: { ... }
  }
}

Vous pouvez désormais récupérer autant de messages que vous le souhaitez en même temps, et les fusionner indépendamment dans le fichier postsById sans se soucier de savoir si le message récupéré est le message actuel.

Dans votre composant, vous lisez toujours state.postsById[state.currentPostId] ou mieux, exporter getCurrentPost(state) du fichier réducteur afin que le composant ne dépende pas d'une forme d'état spécifique.

Maintenant, il n'y a plus de conditions de course et vous disposez d'un cache des messages, ce qui vous évite d'avoir à les récupérer à nouveau lorsque vous revenez en arrière. Plus tard, si vous souhaitez que le message actuel soit contrôlé à partir de la barre d'URL, vous pouvez supprimer la commande currentPostId de l'état Redux complètement, et à la place le lire à partir de votre routeur - le reste de la logique resterait le même.


Bien que ce ne soit pas strictement la même chose, il se trouve que j'ai un autre exemple avec un problème similaire. Regardez le code avant y el code après . Ce n'est pas tout à fait la même chose que votre question, mais j'espère que cela montre comment l'organisation de l'état peut aider à éviter les conditions de course et les props incohérents.

J'ai également enregistré une série de vidéos gratuites qui expliquent ces sujets. vérifiez-le .

4voto

Cam Jackson Points 1155

La solution de Dan est probablement la meilleure, mais une solution alternative consiste à interrompre la première demande lorsque la seconde commence.

Vous pouvez le faire en divisant votre créateur d'action en un créateur asynchrone qui peut lire depuis le magasin et distribuer d'autres actions, ce que redux-thunk vous permet de faire.

La première chose que doit faire votre créateur d'action asynchrone est de vérifier dans le magasin s'il existe une promesse existante, et de l'interrompre s'il y en a une. Si ce n'est pas le cas, il peut faire la demande et envoyer une action 'request begins', qui contient l'objet de la promesse, qui est stocké pour la prochaine fois.

De cette façon, seule la promesse la plus récemment créée sera résolue. Lorsque c'est le cas, vous pouvez envoyer une action de réussite avec les données reçues. Vous pouvez également envoyer une action d'erreur si la promesse est rejetée pour une raison quelconque.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X