Revenez sur les notions de state et de props
Vous rappelez-vous comment créer un composant réutilisable et configurable ?
Revoyons brièvement la différence entre les props et le state, ce qu’ils nous permettent de faire et leurs limites.
En réutilisant nos composants, nous souhaitons la plupart du temps adapter leur comportement selon leur emplacement dans l’application ou selon certaines données provenant du composant parent. Nous utilisons donc à cet effet les props, qui seront appliqués à nos composants.
const IMAGES = [
{
url: "https://unsplash.com/fr/photos/cjSUZMA2iW8",
title: "Un cheval"
},
{
url: "https://unsplash.com/fr/photos/1ZjI3KgB9Co",
title: "Un chien"
}
]
const Container = () => {
return IMAGES.map((image, index) => <Picture url={image.url} title={image.title} key={index} />
}
Ci-dessus, nous utilisons le composant Picture
pour afficher autant d’images différentes provenant d’un tableau IMAGES
en passant en props des valeurs url
et title
distinctes.
Certains composants évoluent au fil du temps. Ils réagissent aux évènements utilisateurs : des actions sur un bouton ou la saisie d’un nombre dans un champ, par exemple.
Le state, ou état local du composant, nous permet de stocker à l’échelle de notre composant des valeurs qui changent et dont les changements ont une incidence sur le comportement de notre composant :
const Counter = () => {
const [count, setCount] = useState(0)
return <div>
<span>Valeur du compteur: {count}</span>
<button onClick={() => setCount(count + 1)}>ajouter</button>
</div>
}
Ci-dessus, nous modifions la valeur d’un compteur qui change à chaque clic sur le bouton “Ajouter”.
En combinant props et state, il nous est donc possible de partager des valeurs de states entre des composants faisant partie de la même branche, c'est-à-dire ayant un parent commun.
Pour accéder au state d’un parent, un composant doit recevoir en props les valeurs du state du parent. Un composant peut modifier le state de son parent si celui-ci obtient la méthode setState
, qui permet de modifier le state en props. On dit que les props descendent et que l’état remonte :
Ci-dessus, nous partageons la valeur du state du composant parent vers les composants A et B en passant celle-ci en props. Grâce au setState
passé en props, le composant B peut modifier la valeur du state A.
Notez aussi qu’un composant A qui partage le même parent que le composant B ne peut pas accéder au state de celui-ci. C’est une contrainte, puisque faire remonter le state dans le composant parent devient ici l’unique moyen de partager le state entre A et B.
Comprenez le principe de props drilling
Nous venons de voir que l’état peut se distribuer dans les composants par le biais des props. Du fait que deux composants ayant le même parent ne peuvent pas accéder directement à leur state respectif, nous sommes contraints de passer par le state du parent pour le distribuer dans chacun d’eux.
Ce qui suit décrit une seconde contrainte.
Comment faire pour transmettre des données à un composant qui n’est pas enfant direct d’un autre composant ?
On respecte dans ce cas le sens de distribution parent vers enfant et on s’assure de passer les props jusqu’au composant ciblé.
Ci-dessus, nous voyons un exemple de transmission à travers les composants. Notre Props A, appliqué à notre Composant A, traverse ainsi le Composant B pour être appliqué au Composant C.
Nous faisons ici ce qu’on appelle du props drilling, nous avons “percé” un passage dans le composant B pour transmettre la valeur de notre props A au composant C.
Découvrez les limites du props drilling
Nous avons donc un moyen de partager des states dans ce cas dans notre application, pourquoi ne pas utiliser cette méthode partout ?
La raison est à la fois conceptuelle et pratique.
Lorsque nous créons des composants afin de les réutiliser, nous cherchons à les rendre le plus génériques possible. C'est-à-dire suffisamment simples et conçus de manière à être adaptés à beaucoup de situations. Nous essayons alors de limiter la responsabilité de chaque composant à ses fonctionnalités propres.
Lorsque nous utilisons le props drilling, nos composants intermédiaires (ici Composant B) doivent recevoir des props et les transmettre même si ces données ne sont pas rattachées à leur logique propre. Nous donnons ainsi à ces composants trop de responsabilités.
De plus, si nous multiplions la réutilisation de nos composants intermédiaires avec des composants enfants qui attendent des props différents à chaque fois, nous augmentons le nombre de props à faire traverser.
Ci-dessus, nous représentons la réutilisation du composant B dans 2 situations. Le composant B doit faire redescendre Props A, Props B et Props C, et ce, même s’ils ne sont pas utilisés dans tous les cas.
On comprend très vite que mis à l’échelle dans une application qui contient des dizaines de composants à faire communiquer, avec plusieurs composants intermédiaires, l’exercice peut s’avérer difficile.
Gérez le state depuis un store centralisé
Eh oui, distribuer les valeurs via les props peut devenir un vrai casse-tête. Alors, comment gérer le state ?
À l’image d’une étagère remplie de livres dans lesquels des élèves se servent au moment voulu, peut-on imaginer stocker les données à un seul endroit et connecter chaque composant qui aurait besoin de ceux-ci ?
C’est ce que propose Dan Abramov via la librairie Redux qui s’inspire de l’architecture Flux. D’autres librairies s’inspirent ou sont des alternatives de cette architecture, comme MobX ou Recoil. Vous pouvez retrouver le projet archivé de Flux sur GitHub.
Le principe de l’architecture flux est le suivant :
nous stockons un état général dans ce qu’on appelle des stores ;
les composants qui ont besoin d’une valeur de ce state viennent consulter le store et s’abonner aux changements de ce state ;
le store possède un mécanisme qui à chaque changement du state, va informer chaque composant abonné qu’un changement a eu lieu ;
chaque composant peut alors relire le contenu du state et mettre à jour son état local.
Ainsi nous stockons l’état à l’extérieur de notre application qui vient s’alimenter le moment voulu.
Nous reviendrons plus en détail sur cette architecture dans le prochain chapitre, lorsque nous mettrons en place notre propre implémentation.
En résumé
Les données des props et des states ne peuvent être communiquées que dans le sens descendant – du parent vers l'enfant.
Il est donc possible de communiquer des états entre les composants, mais cela comporte des limites.
L’utilisation d’un store centralisé peut permettre de répondre aux problématiques de partage de states entre composants.
Voyons voir maintenant comment mettre en place un store centralisé en créant notre propre modèle basé sur l’architecture Flux.