Redux: Question: Comment choisir entre le magasin de Redux et l'état de React?

Créé le 27 janv. 2016  ·  26Commentaires  ·  Source: reduxjs/redux

question

Commentaire le plus utile

Utilisez React pour un état éphémère qui n'a pas d'importance pour l'application dans le monde et ne mute pas de manière complexe. Par exemple, une bascule dans un élément d'interface utilisateur, un état d'entrée de formulaire. Utilisez Redux pour un état qui compte globalement ou qui subit des mutations complexes. Par exemple, les utilisateurs mis en cache ou un post-projet.

Parfois, vous voudrez passer de l'état Redux à l'état React (lorsque le stockage de quelque chose dans Redux devient gênant) ou l'inverse (lorsque plus de composants doivent avoir accès à un état qui était local).

La règle d'or est la suivante: faites ce qui est moins gênant.

Tous les 26 commentaires

Utilisez React pour un état éphémère qui n'a pas d'importance pour l'application dans le monde et ne mute pas de manière complexe. Par exemple, une bascule dans un élément d'interface utilisateur, un état d'entrée de formulaire. Utilisez Redux pour un état qui compte globalement ou qui subit des mutations complexes. Par exemple, les utilisateurs mis en cache ou un post-projet.

Parfois, vous voudrez passer de l'état Redux à l'état React (lorsque le stockage de quelque chose dans Redux devient gênant) ou l'inverse (lorsque plus de composants doivent avoir accès à un état qui était local).

La règle d'or est la suivante: faites ce qui est moins gênant.

Pour les données récupérées via des requêtes réseau, utilisez toujours les magasins afin de prendre en charge le rendu côté serveur (si vous pensez que c'est le but ultime), sinon vous ne pourrez pas vous réhydrater.

Pour que ces états soient écoutés par deux ou plusieurs conteneurs, il devrait également être dans les magasins?

Je suis d'accord avec @gaeron sur la distinction entre éphémère et persistant. Mais je pense en fait à cela en trois catégories . L'un est l' état de l' état de l' l'état de routage . J'utilise le terme «routage» parce qu'il est familier aux gens, mais je résume cela à un état de «sélection de vues» car je pense que cela couvre mieux le bureau, le Web et le mobile.

Vous pouvez maintenant affirmer qu'il s'agit de l'état de l'interface utilisateur, car il décide de ce que les gens voient (un peu comme l'état des onglets, par exemple). Mais je vois deux distinctions. Le premier est que l'état est sérialisé (c'est-à-dire comme une URL) et envoyé à d'autres personnes. Donc, les choses devraient aller dans l'état de la route si vous voulez que les gens puissent "établir un lien profond" directement vers cet état particulier de l'interface utilisateur . La seconde est que, dans de nombreux cas, l'état initial de l'itinéraire ou un changement d'itinéraire déclenche un changement d'état de l'application (c'est-à-dire, le chargement des données à visualiser). Bien sûr, les actions dans l'interface utilisateur font la même chose. Mais la distinction que je fais est que vous pouvez (et devriez) avoir l'état de la route sans aucune vue ou rendu précisément pour tester la "logique d'application" autour des changements d'état de la route. Et c'est parce que la vue et le rendu n'ont pas besoin d'être impliqués qui en font en partie l'état d'application, à mon humble avis.

Comment cela se rapporte-t-il à la question de Redux vs React pour la gestion des états? L'état de l'application est le domaine de Redux et l'état de l'interface utilisateur est le domaine de React. Mais l'état de routage devrait (à mon avis) être géré par Redux même s'il peut être considéré comme l'état de l'interface utilisateur (voir les liens intégrés pour plus de détails sur les raisons pour lesquelles je pense cela).

Pour être clair, le commentaire de @gaearon sur les choses en mouvement s'applique toujours. Mais je trouve juste utile de distinguer ces différents cas.

L'état de l'application est le domaine de Redux et l'état de l'interface utilisateur est le domaine de React.

Notez que je ne prétends pas cela. Je pense que c'est bien à la fois de conserver un état d'application dans React et un état d'interface utilisateur dans Redux. Je ne pense pas qu'ils devraient être séparés par des domaines.

La façon dont j'y pense, si vous créez une application avec Redux, adoptez l'arborescence d'état unique. Mettez l'état de l'interface utilisateur ici également. Cependant, si cela devient fastidieux et frustrant, n'ayez pas peur de mettre de l'état dans les composants. Mon point est d'utiliser un arbre d'état unique à moins que ce ne soit gênant, et ne le faites que lorsque cela simplifie les choses pour vous plutôt que de les compliquer. C'est la seule ligne directrice.

Premièrement, je ne voulais certainement pas mettre des mots dans la bouche de @gaearon , désolé pour cela.

Deuxièmement, je suis entièrement d'accord avec @gaearon. Je crois aussi fermement en une approche d'arbre à états unique. Quand j'ai parlé de l'état de l'interface utilisateur, j'avais en tête des choses vraiment mineures (comme l'onglet actuel sélectionné) où cela n'était peut-être pas vraiment pertinent pour l'état de l'application (exactement @gaearon a-

Alors laissez-moi clarifier ma position. Je conviens que pratiquement tout devrait être dans Redux , y compris l'état de la route (comme je l'ai fait dans mon implémentation TodoMVC avec Redux et TypeScript . J'ai spécifiquement mentionné l'état de la route et je l'ai distingué parce que je pense (et vous pouvez le voir dans ce TodoMVC implémentation) qu'il appartient définitivement à Redux (et ne devrait pas du tout être connecté au rendu) et n'est certainement pas "état de l'interface utilisateur".

Pour les composants React, j'utilise rarement l'état. Je préfère utiliser des accessoires à la fois pour obtenir des informations sur ce qu'il faut rendre et pour obtenir des fermetures que je peux invoquer pour déclencher des changements externes à mon composant. Il y a quelques circonstances rares où j'introduis un état dans des composants juste pour rendre la vie plus facile, mais j'essaye de l'éviter. J'espère que cela clarifie les choses.

Je pense que cette question est vraiment subjective et compliquée, alors j'ai pris une décision difficile avec mon équipe aujourd'hui, ne vous inquiétez pas:

  • pour un conteneur non réutilisable, qui a une connexion à Redux, il suffit de tout mettre dans le magasin Redux, même le petit état de l'interface utilisateur, comme si un modal est ouvert, n'utilisez plus this.setState .
  • pour les composants réutilisables, qui n'ont rien à voir avec Redux, utilisez l'état React.

Et maintenant, nous implémentons des aides pour rendre la gestion du petit état de l'interface utilisateur moins fastidieuse.

@gaearon @ lionng429 @xogeny Avez -vous des inconvénients à penser à cette approche?

@inetfuture J'ai tendance à être un peu avare à propos de l'état de l'application (redux) parce que j'utilise TypeScript et que je définis un type pour l'état de mon application tout en bas. Cela étant dit, j'ai eu l'occasion de développer des bibliothèques de composants pour séparer toute la manipulation d'état du rendu (voir les liens dans les commentaires précédents pour plus de détails). Cela signifie que même pour les composants généraux qui ne sont pas triviaux, j'extériorise complètement l'état. Je passe généralement tout par des accessoires (à la fois des états et des fermetures pour manipuler l'état). De cette façon, je peux facilement le connecter à l'état de mon application. Une partie de cela est sans aucun doute un artefact de l'utilisation de TypeScript et de l'impossibilité d'utiliser simplement combineReducers et connect pour accrocher les choses (et préserver au moins les contraintes de type). Ce n'est donc probablement pas un point de vue représentatif. Mais je dirai que pourquoi vous dites "il suffit de tout mettre dans le magasin Redux", je craindrais que vous vous retrouviez avec un évier de cuisine d'état. Je pense que le fait que mon utilisation de TypeScript signifie qu'il faut un certain effort pour étendre l'état de l'application n'est pas nécessairement une mauvaise chose car cela me force à décider "ai-je vraiment besoin de cela?" au lieu de laisser les choses s'accumuler.

Autre réflexion: l'état du composant local peut être utile pour les entrées contrôlées qui nécessitent un temps de mise à jour rapide, ainsi que pour réduire le nombre d'actions réelles déclenchées. Dans mon prototype actuel, j'ai mis en place un HOC d'édition de formulaire qui reçoit l'élément en cours d'édition comme accessoire. Il gère également les événements de modification d'entrée de formulaire en enregistrant le champ modifié dans son état, puis en appelant un créateur d'action "EDIT_ITEM_ATTRIBUTE" sans rebond avec les modifications WIP locales combinées. Le résultat est que les champs du formulaire sont mis à jour immédiatement, car seul le formulaire lui-même est en cours de rendu, et beaucoup moins d'actions sont déclenchées (par exemple, si je maintenais 'A' enfoncé pendant quelques secondes, seule la valeur 'AAAAAAAAAAAA' serait être envoyé comme une action plutôt que «A», «AA», etc.).

J'ai le HOC comme un point essentiel sur https://gist.github.com/markerikson/554cab15d83fd994dfab , si quelqu'un s'en soucie.

Quoi qu'il en soit, le fait est que l'état du composant et l'état du magasin ont tous deux leur utilité - vous devez simplement considérer votre cas d'utilisation réel.

Quant aux composants réutilisables, s'ils sont suffisamment grands et complexes, il serait tout à fait à l'épreuve du temps d'avoir leur état dans Redux, principalement pour la traçabilité et la relecture pour les tests. Mais il y a encore des doutes sur la façon dont exactement cette réutilisation de code devrait être architecturée (composants plus actions plus réducteurs).

Dans nos applications, nous avons résolu ce problème dans le style de connexion. https://github.com/Babo-Ltd/redux-state

J'ai rarement trouvé difficile de stocker des états dans Redux. J'aime la puissance qu'il fournit, que plus tard je peux le faire interagir avec un autre composant si j'en ai besoin.

La seule chose qui me vient à l'esprit où ce serait gênant est de gérer l'état des éléments de formulaire, car il peut y en avoir tellement. Dans ce cas, j'utilise une bibliothèque comme redux-form .

Mon point est d'utiliser un arbre d'état unique à moins que ce ne soit gênant, et ne le faites que lorsque cela simplifie les choses pour vous plutôt que de les compliquer. C'est la seule ligne directrice

@gaearon aimeriez -vous partager vos expériences sur la façon de décider quand ou comment cela est considéré comme _awkward_? par exemple, pouvez-vous donner des scénarios / exemples typiques que vous pensez que c'est assez gênant s'ils sont mis en état d'application atom? Merci d'avance!

@idavollen je peux en donner un couple:

  • un état d'ouverture / fermeture de liste déroulante, sinon vous devrez garder une trace de tous les composants de liste déroulante dans votre magasin
  • lorsque vous annulez un changement de <input> , vous pouvez utiliser state pour mettre à jour la valeur instantanément (et vous ne risquez pas que d'autres composants soient rendus inutilement) et mettre à jour le magasin de manière anti-rebond

En gros, vous devriez l'utiliser pour un état qui n'affecte pas les autres composants.

J'ai une troisième situation à considérer, par exemple, j'ai un formulaire (un composant de conteneur de réaction qui a un calcul coûteux dans la méthode de rendu) composé de nombreuses lignes avec une case à cocher précédée et un bouton d'envoi. Lorsque le bouton d'envoi est cliqué, le formulaire doit soumettre les lignes dont les cases à cocher sont cochées, ce qui signifie que les états transitoires de la case à cocher individuelle avant de soumettre le formulaire lorsque l'utilisateur bascule, ce n'est pas important et ne doit pas être traité comme l'état du composant et le basculement de la case à cocher ne doit pas render () à appeler.

Pour le moment, je ne mettrais ni les états de la case à cocher dans l'état de l'application, ni dans l'état du composant local, c'est-à-dire le composant form react afin d'éviter un appel inutile de render () en basculant la case, au contraire, un Il est préférable que la variable d'instance contienne les cases cochées, mais elle est contre react-pattern.

Je me demande quelle est la meilleure approche pour gérer ces états de cases à cocher dans ce cas?

@idavollen ces cases à cocher contrôlées ? Si ce n'est pas le cas, vous les restituez de toute façon, vous pouvez donc garder une trace de ceux qui sont sélectionnés dans l'état du composant et l'utiliser pour sélectionner les parties de données qui seront soumises.

Quelle est la vitesse de Redux par rapport à l'état des composants? Dois-je compter sur Redux pour les accessoires d'interface utilisateur qui sont vraiment judicieux en termes de temps entre l'événement réel et le processus de rendu? J'ai une situation compliquée où l'utilisation de l'état des composants nécessiterait beaucoup de communication pas si simple. Toute aide dans ce domaine serait très appréciée.

@tiberiumaxim D'après mon expérience (je développe des applications en react et redux qui fonctionnent sur des appareils à faible puissance comme les Smart TV). Dans la plupart des cas, la mise à jour de l'état des composants sera plus performante, simplement parce que la différence qui doit se produire concerne un ensemble de données plus petit et qu'aucune distribution ne doit avoir lieu. Dans les cas où seuls le composant ou les enfants ont besoin d'accéder à cet état, il serait préférable de s'en tenir à l'état du composant local et c'est souvent le cas avec l'état lié à l'interface utilisateur, si toutefois vous devez conserver ces données dans le magasin redux à un moment donné - vous devrez peut-être utiliser une combinaison d'état local (composant) vs état global (redux) et assurez-vous de conserver ces données dans le magasin redux au bon moment - c'est-à-dire lorsqu'un utilisateur quitte la page - ou quelque chose à cet effet. Peut-être que si vous pouviez nous éclairer davantage sur votre cas spécifique, je pourrais vous donner des informations plus détaillées

C'est à vous de décider de l'état de Redux et de ce qui reste local pour un composant. Vous pouvez consulter http://redux.js.org/docs/faq/OrganizingState.html#organizing -state-only-redux-state et http://redux.js.org/docs/faq/Performance. html # performance -scaling pour certaines informations connexes.

@deowk merci d'avoir partagé vos connaissances sur la question des performances, je pensais la même chose mais j'avais besoin d'une confirmation.
Aussi, @markerikson merci pour les liens, je vais les revoir une fois de plus.

Je ne sais pas si quelqu'un l'a mentionné explicitement, mais l'état des composants locaux peut également être géré par Redux. Vous pouvez créer un magasin directement dans votre constructeur de composants. Ce magasin local contiendrait l'état de ce composant et gérerait les actions liées à ce composant (et à ses enfants en passant soit la fonction dispatch de ce magasin, soit les rappels qui appellent cette fonction dispatch ) .

Cette architecture peut être implémentée manuellement, ou utiliser une bibliothèque comme redux-fractal, redux-ui et ainsi de suite.

Ou, comme Dan l'a souligné, vous pouvez même implémenter une approche de style réducteur pour mettre à jour l'état d'un composant! Voir https://twitter.com/dan_abramov/status/736310245945933824

Je veux savoir où stocker l'état de l'interface utilisateur comme l'indicateur d'activité et l'ouverture ou la fermeture modale. L'utilisation de setState pose-t-elle un problème lors des tests unitaires des composants de réaction?

@ akshay2604 : encore une fois, c'est à vous de setState lors du test des composants, surtout si vous utilisez la bibliothèque Enzyme pour vous aider à les tester.

Laissez-moi partager mes pensées.

Je classe l'état en 3 grandes catégories en fonction de son lien avec l'entrée / sortie de l'application:

  • "cache": un état qui est un miroir de données persistantes. Exemple:
{ "todos": [{ id: 1, title: "title" }, { id: 2, title: "title" }] }
  • «modifications»: un état qui décrit les modifications en attente des données persistantes. Exemple:
{ "changeTodo": { title: "title", action: "add", done: false },
{ id: 1, title: "changed title", action: "modify", done: true }, { id: 2, action: "delete" } }
  • "vue": un état qui transmet des options de vue comme le filtrage actuel, le tri, etc. Exemple:
{ "displayOptions": { searchTerm: "title", sort: ["title", "desc"], filter: "done=false" } }

Vous ne mélangez pas cache avec changes , vous ne mélangez pas cache avec view , vous ne mélangez pas view avec changes .

Bien sûr, vous devez les fusionner pour les afficher dans un seul composant de réaction. Pour que votre objet "fusionné" devienne:

{ "todos": [{id: undefined, title: "title", done: false }, { id: 1, title: "changed title" }] }

Mais ce n'est pas un état, c'est le résultat de la logique de votre
Vous pouvez mettre en cache cet état fusionné dans les sélecteurs, vous pouvez le mettre en cache dans le magasin, vous pouvez le mettre en cache dans les composants, mais ce n'est pas un "état", ce n'est pas important, vous pouvez facilement réappliquer votre fonction logique d'application aux 3 éléments d'état et obtenir à nouveau un état fusionné. C'est ça l'approche fonctionnelle.

Les responsables de Redux vous diront "Hé, vous devez implémenter la logique de votre application sous la forme de plusieurs réducteurs et de multiples événements, qui construiront différentes parties de votre objet" fusionné "."
Je pense que c'est une mauvaise approche. Cela signifie que vous ne pourrez pas vous éloigner de redux. Ce qui était censé être un outil pour gérer votre état devient essentiellement une implémentation de votre logique applicative .

Bonjour, je fouille dans React / Redux pendant quelques jours, je comprends la décision prise de choisir si certaines informations doivent être promues pour stocker ou conservées dans l'état local du composant.

Mais que se passe-t-il si une action nécessite les deux simultanément? Voici le cas d'utilisation, qui est très courant:

  1. [contexte] Le magasin Redux est une seule feuille { modelItems: myModelItems } . Il met en cache (une partie / une projection de) le modèle de serveur
  2. [contexte] La vue est constituée d'un seul composant intelligent MyItemsView qui affiche une liste d'interface utilisateur des éléments stockés / mis en cache. Une fois monté, le composant déclenche une extraction des éléments de modèle. Entrée de composant: myModelItems . Sortie du composant: un déclencheur d'action fetchModelItems . Composants internes: state.busy et state.errorId
  3. [plan] Lorsque MyItemsView déclenche fetchModelItems , une requête HTTP est envoyée au serveur afin de récupérer le modèle et de le mettre en cache en magasin. Cette action asynchrone comporte 4 phases et autant de hooks: onStarted (la requête HTTP est sur le point de décoller, le chargement du spinner démarre dans le composant), onEnded (réponse HTTP atterri, le chargement du spinner s'arrête dans le composant) , on ne regarde toujours pas l'état de la réponse), onFailed (une notification d'erreur est affichée dans le composant) et onSucceed (le magasin Redux est mis à jour puis le cache-modèle qu'il contient est transmis à le composant pour le rendu)

Par conséquent, comment diviser le déclencheur d'action fetchModelItems pour que:

  • onStarted , onEnded et onFailed ne sont pas disponibles dans la boutique Redux? Ceci parce que onStarted et onEnded sont des états d'interface utilisateur transitoires qui ne devraient avoir aucune interaction avec le cache de modèle. Il en va de même pour onFailed (le cache du modèle reste inchangé et l'interface utilisateur affichera une notification d'erreur). MyItemsView affichera l'état de l'interface utilisateur ici, pas le modèle mis en cache
  • onSucceed arrive dans la boutique Redux? Ceci parce qu'il a remporté la loterie du cache modèle, a accès à la source de la vérité, se douchera et se réhydratera, puis rendra le frais myModelItems en MyItemsView

À propos de l'implémentation dans ma tentative actuelle, Redux Thunk prend en charge le déclencheur d'action fetchModelItems . J'ai l'impression d'avoir choisi le chemin Redux là-bas et que je ne peux plus changer d'avis pour dire "transmettez simplement le succès asynchrone au magasin Redux, gardez tout le reste dans l'état du composant React".

Intrigué par cela, je récupère les succès à stocker, qui les retransmet au composant connecté (ie conteneur Redux). Mais pas de spinner de chargement et un misérable atm de message d'erreur consigné par la console. Je ne veux pas définir store.ui.myItemsView.busy ni store.ui.myItemsView.errorId , mais simplement obtenir ces informations dans l'état du composant.

@pascalav :
Ceci est un bug tracker, pas un système de support. Pour les questions d'utilisation, veuillez utiliser Stack Overflow ou Reactiflux où il y a beaucoup plus de personnes prêtes à vous aider - vous obtiendrez probablement une meilleure réponse plus rapidement. Merci!

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

vslinko picture vslinko  ·  3Commentaires

ms88privat picture ms88privat  ·  3Commentaires

CellOcean picture CellOcean  ·  3Commentaires

captbaritone picture captbaritone  ·  3Commentaires

jbri7357 picture jbri7357  ·  3Commentaires