Je suis nouveau sur React / Redux. J'utilise un middleware fetch api dans l'application Redux pour traiter les API. C'est ( redux-api-middleware ). Je pense que c'est le bon moyen de traiter les actions d'API asynchrones. Mais je trouve des cas qui ne peuvent être résolus par moi-même.
Comme l'indique la page d'accueil ( cycle de vie ), un cycle de vie d'API d'extraction commence par l'envoi d'une action CALL_API se termine par l'envoi d'une action FSA.
Mon premier cas est donc d'afficher / de masquer un préchargeur lors de la récupération des API. L'intergiciel enverra une action FSA au début et une action FSA à la fin. Les deux actions sont reçues par des réducteurs qui ne devraient effectuer qu'un traitement normal des données. Aucune opération d'interface utilisateur, plus d'opérations. Peut-être que je devrais enregistrer l'état du traitement dans l'état puis les rendre lors de la mise à jour du magasin.
Mais comment faire ça? Un flux de composants de réaction sur toute la page? que se passe-t-il avec la mise à jour du magasin à partir d'autres actions? Je veux dire, ce sont plus des événements que des états!
Même dans le pire des cas, que dois-je faire lorsque je dois utiliser la boîte de dialogue de confirmation native ou la boîte de dialogue d'alerte dans les applications redux / react? Où doivent-ils être mis, actions ou réducteurs?
Meilleurs vœux! Souhait de répondre.
la source
Réponses:
Je ne le dirais pas. Je pense que les indicateurs de chargement sont un excellent cas d'interface utilisateur qui se décrit facilement comme une fonction d'état: dans ce cas, d'une variable booléenne. Bien que cette réponse soit correcte, j'aimerais fournir un code pour l'accompagner.
Dans l'
async
exemple du repo Redux , le réducteur met à jour un champ appeléisFetching
:Le composant utilise
connect()
de React Redux pour s'abonner à l'état du magasin et retourneisFetching
dans le cadre de lamapStateToProps()
valeur de retour afin qu'il soit disponible dans les accessoires du composant connecté:Enfin, le composant utilise
isFetching
prop dans larender()
fonction pour rendre une étiquette «Loading ...» (qui pourrait éventuellement être un spinner à la place):Les effets secondaires (et l'affichage d'une boîte de dialogue est très certainement un effet secondaire) n'appartiennent pas aux réducteurs. Pensez aux réducteurs comme à des «bâtisseurs d'État» passifs. Ils ne «font» pas vraiment les choses.
Si vous souhaitez afficher une alerte, faites-le depuis un composant avant d'envoyer une action, ou faites-le depuis un créateur d'action. Au moment où une action est envoyée, il est trop tard pour effectuer des effets secondaires en réponse.
Pour chaque règle, il y a une exception. Parfois, votre logique d'effets secondaires est si compliquée que vous voulez en fait les coupler soit à des types d'action spécifiques, soit à des réducteurs spécifiques. Dans ce cas, consultez des projets avancés tels que Redux Saga et Redux Loop . Ne faites cela que lorsque vous êtes à l'aise avec la vanille Redux et que vous avez un réel problème d'effets secondaires dispersés que vous aimeriez rendre plus gérables.
la source
Promise.all
en une seule promesse, puis envoyer une seule action pour toutes les extractions. Ou vous devez maintenir plusieursisFetching
variables dans votre état.isFetching
drapeau. Il est défini pour chaque ensemble d'objets en cours de récupération. Vous pouvez utiliser la composition du réducteur pour implémenter cela.RECEIVE_POSTS
n'est jamais déclenchée, le signe de chargement restera en place sauf si vous avez créé une sorte de délai d'expiration pour afficher unerror loading
message.Grande réponse Dan Abramov! Je veux juste ajouter que je faisais plus ou moins exactement cela dans l'une de mes applications (en gardant isFetching comme un booléen) et que j'ai fini par en faire un entier (qui finit par lire comme le nombre de demandes en suspens) pour prendre en charge plusieurs simultanées demandes.
avec booléen:
request 1 démarre -> spinner on -> request 2 start -> request 1 ends -> spinner off -> request 2 ends
avec un entier:
demande 1 démarre -> spinner on -> demande 2 démarre -> demande 1 se termine -> demande 2 se termine -> spinner off
la source
isFetching
indicateur. Si vous regardez de près l'exemple que j'ai lié, vous verrez qu'il n'y a pas un objet avecisFetched
mais plusieurs: un par sous-reddit (ce qui est récupéré dans cet exemple).fetchCounter
barre de progression globale pour certaines en haut de l'écran et plusieursisFetching
indicateurs spécifiques pour les listes et les pages.J'aimerais ajouter quelque chose. L'exemple du monde réel utilise un champ
isFetching
dans le magasin pour représenter le moment où une collection d'éléments est extraite. Toute collection est généralisée à unpagination
réducteur qui peut être connecté à vos composants pour suivre l'état et montrer si une collection est en cours de chargement.Il m'est arrivé que je voulais récupérer les détails d'une entité spécifique qui ne rentre pas dans le modèle de pagination. Je voulais avoir un état représentant si les détails sont récupérés sur le serveur, mais je ne voulais pas non plus avoir de réducteur juste pour cela.
Pour résoudre ce problème, j'ai ajouté un autre réducteur générique appelé
fetching
. Il fonctionne de la même manière que le réducteur de pagination et sa responsabilité est simplement de regarder un ensemble d'actions et de générer un nouvel état avec des paires[entity, isFetching]
. Cela permet auconnect
réducteur de n'importe quel composant et de savoir si l'application récupère actuellement des données non seulement pour une collection mais pour une entité spécifique.la source
Je ne suis pas tombé sur cette question jusqu'à présent, mais comme aucune réponse n'est acceptée, je vais jeter mon chapeau. J'ai écrit un outil pour ce travail: react-loader-factory . Il y a un peu plus de travail que la solution d'Abramov, mais il est plus modulaire et pratique, car je ne voulais pas avoir à réfléchir après l'avoir écrit.
Il y a quatre gros morceaux:
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
connect()
revient dans Redux), de sorte que vous pouvez simplement le boulonner sur votre matériel existant.const LoadingChild = loaderWrapper(ChildComponent);
ACTION_SUCCESS
etACTION_REQUEST
, par exemple). (Vous pouvez envoyer des actions ailleurs et simplement surveiller depuis le wrapper si vous le souhaitez, bien sûr.)Le module lui-même est indépendant de redux-api-middleware, mais c'est avec cela que je l'utilise, alors voici un exemple de code du README:
Un composant avec un chargeur l'enveloppant:
Un réducteur que le chargeur doit surveiller (bien que vous puissiez le câbler différemment si vous le souhaitez):
Je pense que dans un proche avenir, j'ajouterai des éléments tels que le délai d'expiration et l'erreur au module, mais le modèle ne sera pas très différent.
La réponse courte à votre question est:
la source
Suis-je le seul à penser que les indicateurs de chargement n'appartiennent pas à un magasin Redux? Je veux dire, je ne pense pas que cela fasse partie de l'état d'une application en soi.
Maintenant, je travaille avec Angular2, et ce que je fais, c'est que j'ai un service "Loading" qui expose différents indicateurs de chargement via RxJS BehaviourSubjects .. Je suppose que le mécanisme est le même, je ne stocke tout simplement pas les informations dans Redux.
Les utilisateurs de LoadingService s'abonnent simplement aux événements qu'ils souhaitent écouter.
Mes créateurs d'action Redux appellent le LoadingService chaque fois que les choses doivent changer. Les composants UX s'abonnent aux observables exposés ...
la source
Vous pouvez ajouter des écouteurs de changement à vos magasins, à l'aide
connect()
de React Redux ou de lastore.subscribe()
méthode de bas niveau . Vous devriez avoir l'indicateur de chargement dans votre magasin, que le gestionnaire de changement de magasin peut ensuite vérifier et mettre à jour l'état du composant. Le composant restitue ensuite le préchargeur si nécessaire, en fonction de l'état.alert
etconfirm
ne devrait pas être un problème. Ils bloquent et alert ne prend même aucune entrée de l'utilisateur. Avecconfirm
, vous pouvez définir l'état en fonction de ce sur quoi l'utilisateur a cliqué si le choix de l'utilisateur doit affecter le rendu des composants. Sinon, vous pouvez stocker le choix en tant que variable membre du composant pour une utilisation ultérieure.la source
addNofication(message)
. Un autre cas est celui des préchargeurs qui sont également fournis par l'application native de l'hôte et déclenchés par son API javascript. J'ajoute un wrapper pour ces api, danscomponentDidUpdate
un composant React. Comment concevoir les accessoires ou l'état de ce composant?Nous avons trois types de notifications dans notre application, qui sont toutes conçues comme des aspects:
Tous les trois se trouvent au niveau supérieur de notre application (principale) et sont câblés via Redux, comme indiqué dans l'extrait de code ci-dessous. Ces accessoires contrôlent l'affichage de leurs aspects correspondants.
J'ai conçu un proxy qui gère tous nos appels d'API, ainsi toutes les erreurs isFetching et (api) sont médiatisées avec actionCreators que j'importe dans le proxy. (En passant, j'utilise également webpack pour injecter une maquette du service de support pour les développeurs afin que nous puissions travailler sans dépendances de serveur.)
Tout autre endroit de l'application qui doit fournir un type de notification importe simplement l'action appropriée. Snackbar & Error ont des paramètres pour les messages à afficher.
) exporter la classe par défaut Main étend React.Component {
la source
J'enregistre les URL telles que ::
Et puis j'ai un sélecteur mémorisé (via reselect).
Pour rendre l'url unique en cas de POST, je passe une variable comme requête.
Et là où je veux afficher un indicateur, j'utilise simplement la variable getFetchCount
la source
Object.keys(items).filter(item => items[item] === true).length > 0 ? true : false
parObject.keys(items).every(item => items[item])
en passant.some
au lieu deevery
, mais oui, trop de comparaisons non nécessaires dans la première solution proposée.Object.entries(items).some(([url, fetching]) => fetching);