Gérer l’état global d’une application
Au cours d’articles précédents, nous avons réimplémenté de façon naïve la librairie Redux, une librairie majeure dans l’écosystème React. Majeure car elle implémente de façon élégante le pattern flux développé par Facebook et qu’elle possède des features très sympas comme le time traveling (permettant de refaire l’historique des modifications du store Redux) associé au Redux devTool offrant une très bonne developer experience!
Il existe pas mal d’alternatives comme MobX, Unstated ou même - dans une certaine mesure - l’API context de React 16.3 qui est maintenant officielle !
Mais ici, nous allons voir comment pousser un peu plus la logique du state pour avoir une application qui possède bel et bien un state global avec… l’URL de l’application !
URL FTW !
Dans les Single Page Application, contrairement aux applications client/serveur classique, l’URL d’accès n’a pas beaucoup d’importance. Une fois que l’on arrive sur l’application, le javascript s’exécute et le navigateur ne va jamais se refraîchir. Toutes les interactions se passeront sur cette page. Le serveur ne sera appelé que via des requêtes AJAX.
Le fait d’arriver sur https://monApp.fr
ou https://monApp.fr?user=gael&job=dev
ne change rien dans le cas d’une SPA qui n’utilise pas l’URL.
Pourtant, l’URL peut être utilisée comme une sauvegarde de l’état de notre application où chacune des mises à jour de l’URL va correspondre à une mise à jour de ce même état
Dans ces conditions, le fait d’arriver sur https://monApp.fr
ou https://monApp.fr?user=gael&job=dev
aura un impact. Cela permettra d’ouvrir l’application dans un état particulier.
Du code pour les braves !
J’ai commencé à implémenter cette idée sur un side project que j’ai développé dont voici le repo Github et le lien pour jouer avec :)
Dans le cadre de cet article, j’ai cependant redéveloppé un codesandbox pour avoir un code minimaliste pour se concentrer sur le sujet de cet article uniquement.
Tout d’abord, qu’est-ce qui nous intéresse dans l’URL ? Toute la partie de l’URL après le ?.
Ce qu’on appelle les search params.
Un peu de doc par ici sur le sujet.
Récupérer des éléments de l’URL
Pour notre exemple, nous n’allons manipuler qu’un seul paramètre dans l’URL : query.
Pour le récupérer (si tant est qu’il soit effectivement présent dans l’URL), on va utiliser une propriété de l’objet window.location.search
qui correspond à toute la chaîne de caractères après le ?.
Ainsi avec l’url suivante: www.first-contrib?query=react
, on obtient :
console.log(window.location.search); // "?query=react"
Idéalement, plutôt qu’un string
, il serait préférable de manipuler un objet correctement formatté. Pour cela, on va utiliser l’objet URLSearchParams
accessible dans les navigateurs récents. Si non, vous pouvez le polyfiller via cette librairie par exemple.
En code cela donne cela :
|
|
ainsi,
|
|
Parfait, on peut récupérer un objet depuis notre URL. Maintenant comment faire fonctionner ça avec notre application qui intègre la librairie react router pour la gestion du routing ?
On va tenter d’afficher l’objet que l’on obtient depuis l’URL en créant un router
qui affichera un composant prenant pour props
les éléments de l’URL.
|
|
En l’état, on a bien un router qui affiche le composant MainPage
à l’URL “/“ mais on ne sais pas encore récupérer la valeur de query
éventuellement présente dans l’URL.
Pour obtenir query
, on va devoir jouer avec la fonction getParams
que l’on a créé précédemment ainsi que les props
que le composant MainPage
reçoit de façon implicite via le composant Route
à cette ligne : <Route path="/" component={MainPage} />
Si on logge l’objet props
que reçoit MainPage
, on voit qu’il ressemble à ça :
{match: Object, location:Object, history: Object, /*d'autres valeurs */}
et Ô miracle, ce props
possède l’objet location
, similaire au window.location
qu’on a manipulé tout à l’heure ! Ainsi, on peut récupérer les valeurs de l’URL dans MainPage
. On va donc mettre à jour le code de MainPage
en conséquence:
|
|
Maintenant, le composant MainPage
rend des éléments en fonction de l’URL!
Question subsidiare : Quel est l’intérêt de manipuler
location
à travers react router s’il existe déjà dans l’objetwindow
comme on a vu précédemment ?Et bien, avec React les composants réagissent à des modifications de state interne ou de props externes. React Router, lui, permet de relier l’état de l’app à l’URL du navigateur. Ainsi, une modification de l’URL lance une mise à jour de l’application.
Mettre à jour l’URL (et donc le state) !
Maintenant que l’on est capable de lire l’URL du navigateur, on va tenter de la mettre à jour depuis notre application.
On va créer un composant qui va modifier l’URL en fonction de la valeur d’un input
:
|
|
En l’état, ce composant se contente d’afficher une valeur mais est incapable de modifier l’URL. Ce que l’on souhaite, c’est que l’évènement click
sur le bouton récupère les valeurs de l’input et mette à jour l’URL en conséquence. On va donc devoir écrire la fonction du onClick
de l’input de type button.
Pour rappel, on a vu plus haut que l’objet props
que reçoit MainPage
ressemble à ça :
{match: Object, location:Object, history: Object, /*d'autres valeurs */}
Plus précisément, il contient un objet history
décrit dans la documentation de React Router ici…
Ici, nous allons tout particulièrement nous intéresser à la fonction push
qui selon la doc de React Router :
Pushes a new entry onto the history stack
Autrement dit, push
va permettre de mettre à jour l’URL !
Seulement, si la valeur query
vaut “javacript” par exemple, il faut que la mise à jour de l’URL donne www.monApp.fr?query=javascript
. Il faut donc que l’on génère les searchParams
de notre nouvelle URL. Pour ça, l’objet URLSearchParams
va nous aider encore une fois !
Après la fonction getParams
on va donc écrire la fonction setParams
.
|
|
Ainsi, si on utilise la fonction comme suit:
|
|
Maintenant, dans notre input, on va modifier la fonction onClick
:
|
|
Maintenant, si on change la valeur de l’input et que l’on clique sur le bouton, on a bien l’URL qui se met à jour ainsi que le composant MainPage
qui affiche la nouvelle valeur !
Une des choses vraiment sympas avec le stockage de l’état de l’app dans l’URL, c’est le copier/coller de lien. Avec toutes les infos déjà présentes dans l’URL, l’application affiche directement l’état dans lequel on souhaite la trouver ! Essayez en changeant la valeur de l’URL dans le navigateur !
Dans le cas d’un moteur de recherche, on peut lancer la recherche dès le chargement de l’application par exemple. Dans mon application j’utilise react-apollo mais de façon naïve, on peut implémenter la même chose avec n’importe quel client HTTP.
Essayons ici avec la librairie Axios
et l’API Github REST qui ne nécessite pas d’authentification pour fonctionner de créer un composant qui lance des requêtes des éléments en fonction des props
qu’ils reçoient:
|
|
Ce composant ResultsPage
récupère le props query
que l’on récupère depuis l’URL (je crois qu’on a compris depuis le temps) et lance directement une requête au montage du composant avec le lifecycle componentDidMount
ainsi qu’à la mise à jour de la valeur de query
dans avec componentWillReceiveProps
. Et, tadam, on a un composant qui requête automatiquement des valeurs au changement de la valeur de l’URL !!
Pour rappel un codesandbox est là pour tester en live.
Dans notre cas, nous n’avons qu’une valeur mais cela devient très sympa si vous voulez gérer la pagination, un filter sur votre recherche, le tri descendant ou ascendant, …, bref tous les paramètres qu’acceptent votre API !
Dans tous les cas, le fonctionnement est le même, les éléments qui modifient le state modifient l’URL qui se propagent dans tous les enfants pour arriver au composant qui effectue la requête !
Conclusion
Voilà, c’est tout pour moi ! React et le state, un univers impitoyable mais dans lequel il y a la possibilité des choses à faire pour s’amuser et fournir des expériences utilisateurs sympas et différentes.
J’espère que cet article vous aura plus, n’hésitez pas à laisser des commentaires.