Menu

Faire remonter l'état en React : partager l'état

Faire remonter l'état consiste à sortir l'état de deux composants pour le placer dans leur parent commun le plus proche, qui redescend la valeur et un setter sous forme de props. Découvrez quand le faire, comment le faire en trois étapes, et quand garder plutôt l'état local.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Faire remonter l'état consiste à sortir l'état des composants qui doivent le partager pour le placer dans leur parent commun le plus proche. Le parent garde la valeur, la redescend en prop, et passe une fonction que les enfants appellent pour la modifier : chaque enfant fait ainsi son rendu à partir d'une seule copie de l'état.

Cliquez sur "Show" dans n'importe quel panneau : il s'ouvre et celui qui était ouvert se ferme. Un seul panneau peut être ouvert à la fois parce que les panneaux n'en décident pas eux-mêmes. App détient openIndex, et chaque Panel reçoit seulement isOpen et une fonction onOpen.

Le problème : un état qui doit concorder

Partez de la version évidente, où chaque panneau possède son propre état isOpen. Chaque panneau fonctionne, mais les panneaux ne savent rien les uns des autres, donc rien n'empêche deux d'entre eux d'être ouverts en même temps.

Ouvrez les deux panneaux : ils restent ouverts ensemble. Dans React, l'état est privé au composant qui le déclare, un composant frère ne peut donc ni le lire ni le réinitialiser. Quand deux composants doivent concorder, l'état doit vivre au-dessus des deux.

Faire remonter l'état en trois étapes

Transformer le deuxième exemple en premier demande trois modifications.

  1. Retirez l'état de l'enfant. Supprimez useState dans Panel et lisez plutôt isOpen depuis les props. L'enfant ne décide plus s'il est ouvert.
  2. Passez depuis le parent la valeur et un moyen de la modifier. Panel reçoit isOpen et un callback onOpen. L'enfant appelle onOpen() quand on clique sur son bouton ; il ignore ce que le parent en fait.
  3. Ajoutez l'état au parent commun. App déclare openIndex et le transforme en props pour chaque panneau : isOpen={openIndex === 1} et onOpen={() => setOpenIndex(1)}.

Le parent commun le plus proche est le composant le plus bas qui affiche tous les composants ayant besoin de l'état. Ici, c'est App. Si les panneaux se trouvaient dans un composant Faq, l'état irait dans Faq, pas plus haut.

Après la remontée, Panel est contrôlé par son parent au même sens qu'un champ contrôlé : il affiche ce que disent les props et signale les changements par un callback. La page sur les composants contrôlés et non contrôlés traite de la même idée pour les éléments de formulaire.

Une source unique de vérité

Quand deux parties de l'écran affichent le même fait, stockez ce fait une seule fois et calculez tout le reste à partir de lui. Un convertisseur de température est le cas classique : les champs Celsius et Fahrenheit doivent toujours concorder, ils ne peuvent donc pas garder chacun leur propre nombre.

Tapez dans l'un ou l'autre champ et l'autre suit. L'état est un seul fait : le nombre que l'utilisateur a tapé en dernier et l'échelle dans laquelle il l'a tapé. L'autre champ est calculé à partir de lui pendant le rendu, donc le champ dans lequel vous tapez garde toujours exactement ce que vous avez tapé. Remplacez l'état de départ par { value: '212', scale: 'f' } et le message sous les champs devient "Water boils."

Garder un nombre en Celsius et un nombre en Fahrenheit dans deux éléments d'état obligerait chaque gestionnaire à mettre à jour les deux, et la première fois qu'un gestionnaire oublie, les deux champs se contredisent. Une seule valeur stockée ne peut pas se contredire elle-même.

Passer le setter vers le bas

Un enfant ne peut modifier l'état du parent qu'au moyen d'une fonction que le parent lui donne. Vous pouvez passer le setter lui-même (onSelect={setColor}) ou une fonction qui en fait plus (onOpen={() => setOpenIndex(1)}). Donner à la prop un nom de type événement comme onSelect ou onChange, plutôt que setSelected, laisse l'enfant ignorer comment le parent stocke la valeur, et le parent peut donc changer cela plus tard sans toucher à l'enfant.

ColorPicker et Preview ne communiquent jamais directement. Le sélecteur signale un choix vers le haut, App le stocke, et la nouvelle valeur redescend vers les deux.

Quand ne pas faire remonter l'état

Faire remonter l'état a un coût. Quand l'état vit dans un parent, chaque changement fait refaire un rendu au parent et, par défaut, à tous ses enfants, y compris ceux qui n'utilisent pas l'état. Ne remontez pas plus haut que le parent commun le plus proche, et laissez dans un composant l'état que lui seul utilise.

Tapez dans le champ et regardez la console : seul SearchBox refait un rendu à chaque frappe. Déplacez maintenant query dans App et passez-le à SearchBox en props. Chaque frappe affiche alors aussi ProductList, même si la liste n'utilise pas la recherche. Si la liste filtrait selon la recherche, faire remonter l'état serait le bon choix, puisque les deux composants dépendraient alors de la même valeur.

La question à se poser est « qui a besoin de lire cette valeur ? ». Si la réponse est un seul composant, l'état reste là. Si c'est plusieurs, il va dans leur parent commun le plus proche.

Quand la remontée va trop loin

Parfois, le parent commun le plus proche est très haut dans l'arbre, et la valeur doit traverser plusieurs composants qui ne font que la transmettre. C'est le prop drilling. Quelques couches de props, c'est très bien et facile à suivre. Quand la même valeur traverse de nombreuses couches, ou que presque tous les composants en ont besoin (l'utilisateur connecté, le thème, la langue), lisez-la avec useContext au lieu de la passer à la main. Le contexte change la façon dont la valeur atteint les enfants ; l'état lui-même vit toujours dans un seul parent, il est donc toujours remonté.

Questions fréquentes

Que signifie faire remonter l'état en React ?

Déplacer un élément d'état des composants qui l'utilisent vers leur parent commun le plus proche. Le parent possède l'état et passe la valeur, plus une fonction pour la modifier, aux enfants sous forme de props.

Comment deux composants frères partagent-ils un état en React ?

Des composants frères ne peuvent pas lire l'état l'un de l'autre. Placez l'état dans leur parent commun, passez la valeur aux deux, et passez un setter (ou un gestionnaire comme onChange) à celui qui la modifie. Les deux frères font alors leur rendu à partir de la même valeur.

Comment un composant enfant met-il à jour l'état du parent ?

Le parent passe une fonction en prop, par exemple onSelect={setSelected} ou onSelect={(id) => setSelected(id)}, et l'enfant l'appelle. L'état reste dans le parent ; l'enfant ne fait que demander le changement.

Quand ne faut-il pas faire remonter l'état ?

Quand un seul composant utilise l'état. Le placer plus haut que nécessaire fait refaire un rendu au parent à chaque changement et disperse des props dans des composants qui ne s'en servent pas. Gardez l'état aussi près que possible de l'endroit où il est utilisé.

Quelle est l'alternative quand l'état remonte trop haut ?

Si vous passez les mêmes props à travers de nombreuses couches, lisez plutôt la valeur partagée avec le contexte (useContext), ou réorganisez pour rapprocher les composants qui en ont besoin.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

COMMENCER