Comment synchroniser les parameters de requête d’état et d’URL Redux

J’ai une page Web avec un panneau de recherche. Le panneau de recherche comporte plusieurs champs de saisie: id, taille, …

Ce que je veux, c’est lorsque l’utilisateur définit des valeurs de recherche (par exemple: id = 123 et taille = 45) et appuie sur un bouton de recherche:

  1. searchState dans Redux réducteur doit être mis à jour avec de nouvelles valeurs de recherche (id = 123 et taille = 45)
  2. L’URL doit être remplacée par ” http: // mydomain / home? Id = 123 & size = 45 ”

Et d’autre part si l’utilisateur a changé d’URL en http: // mydomain / home? Id = 111 & size = 22 lorsque:

  1. searchState dans le réducteur doit être changé avec de nouvelles valeurs de recherche (id = 111 et taille = 22)
  2. Les entrées du panneau de recherche de l’interface utilisateur doivent être mises à jour avec de nouvelles valeurs (id = 111 et taille = 22)

Comment atteindre avec objective? Quel routeur dois-je utiliser (react-router, redux-router, react-router-redux ou autre)?

Je suggère simplement d’utiliser directement React Router et de ne pas conserver searchState dans Redux. React Router injectera des parameters d’URL dans vos composants et vous pourrez les utiliser dans mapStateToProps(state, ownProps) pour calculer les accessoires finaux.

Si vous voulez vraiment voir les modifications de route comme des actions, vous pouvez utiliser react-router-redux pour la synchronisation bidirectionnelle, mais cela ne vous donnera pas les parameters en l’état, mais uniquement l’emplacement actuel. Le seul cas d’utilisation possible si vous souhaitez enregistrer et rejouer des actions, et que la barre d’URL soit mise à jour lorsque vous les relisez.

Il n’est en aucun cas nécessaire – dans la plupart des cas, il suffit d’utiliser directement React Router.

En bref : vous pouvez utiliser redux-query-sync pour synchroniser les parameters d’URL et les champs du magasin. Cela fonctionne sans n’importe quel routeur.


Histoire plus longue : en luttant contre la même question, j’ai tout d’abord apprécié la réponse de Dan Abramov, en suggérant (selon mes mots) de considérer l’URL comme un «second magasin», une partie de l’état de l’application en dehors du magasin Redux. Mais ensuite, j’ai trouvé que le fait d’avoir deux types de «magasins» rend difficile la modification du code, par exemple pour déplacer des objects de l’un à l’autre, car chaque «magasin» possède une interface différente. En tant que développeur, je préférerais sélectionner l’un des champs dans mon état Redux et les exposer dans l’URL, comme si l’emplacement était une petite fenêtre sur (une partie de) mon état.

Par conséquent, je viens de publier redux-query-sync, pour vous permettre de fournir une fonction de sélection permettant de sélectionner une valeur à partir de l’état, qui est ensuite exposée à l’emplacement de la fenêtre sous un nom de paramètre que vous spécifiez. Pour le laisser également se synchroniser dans l’autre direction, de l’URL à l’état Redux (par exemple lorsque l’application se charge initialement), vous pouvez fournir un créateur d’action auquel sera transmise la valeur du paramètre afin que votre réducteur puisse mettre à jour l’état en conséquence.

Voici comment j’ai géré le premier scénario:

  • Lorsque la valeur d’entrée change, son composant déclenche un rappel qui a été transmis en tant que prop depuis son conteneur.

  • Dans le rappel, le conteneur envoie l’action responsable de la mise à jour de l’état Redux lorsque l’événement se produit.

  • Dans la ligne qui suit immédiatement l’appel d’action, j’utilise this.context.router.push() et transmets l’URL avec la chaîne de requête correcte.

Je ne suis pas certain que ce soit la bonne approche. Je trouve préférable de mettre à jour l’URL d’abord parce que, à mon avis, la chaîne de requête doit être le reflet de l’état plutôt que le maître de celle-ci.

En ce qui concerne le scénario inverse, je ne suis vraiment pas sûr. Il semble que le réglage manuel de l’URL déclenche un rechargement complet, mais je peux me tromper.

En ce qui concerne le routeur à utiliser, j’utilise React Router seul. Je n’ai pas vraiment trouvé de valeur en utilisant les autres et cette discussion a été décisive pour moi.

J’ai récemment terminé de travailler sur un problème similaire à celui-ci. J’ai utilisé MobX comme magasin et redux-routeur comme routeur. (Je me suis bien débrouillé avec react-router, mais je devais envoyer une action push en dehors du composant – redux comme un magasin global fonctionne parfaitement ici)

Ma solution a été similaire à celle décrite par cantera – mon état de routeur ne fait que refléter l’état de la forme. Cela a l’avantage supplémentaire de ne pas avoir à abandonner l’état de mon formulaire et à dépendre entièrement de l’état du routeur.


Dans le premier scénario,

1) Je mets à jour mon entrée de formulaire comme d’habitude, ce qui déclenche un re-render dans le composant results.

2) Dans componentDidUpdate() du composant results, j’utilise les accessoires de formulaire pour construire la chaîne de requête mise à jour.

3) Je pousse la chaîne de requête mise à jour à l’état du routeur. Ceci est purement pour effet de mettre à jour l’URL.


Pour le deuxième scénario

1) Dans le crochet onEnter du composant Route racine, j’obtiens la chaîne de requête disponible et l’parsing dans les valeurs initiales du formulaire.

2) Je mets à jour mon magasin avec les valeurs. Ceci est seulement pour le premier chargement de la page , et le seul moment où l’état du routeur dicte l’état du formulaire.


Edit: Cette solution ne tient pas compte du cas lorsque vous revenez dans l’historique de votre navigateur, car l’URL ne mettra pas à jour votre état.

Je recommanderais le nouvel outil react-url-query qui fera cette synchronisation assez simple.