Articles

Comprendre React `setState`

Les composants React peuvent, et le font souvent, avoir un état. L’état peut être n’importe quoi, mais pensez à des choses comme si un utilisateur est connecté ou non et affiche le nom d’utilisateur correct en fonction du compte actif. Ou un éventail de billets de blog. Ou si un modal est ouvert ou non et quel onglet en son sein est actif.

Les composants React avec l’interface utilisateur de rendu d’état basée sur cet état. Lorsque l’état des composants change, l’interface utilisateur du composant change également.

Cela rend important de comprendre quand et comment changer l’état de votre composant. À la fin de ce tutoriel, vous devez savoir comment fonctionne setState, et être capable d’éviter les pièges courants que beaucoup d’entre nous rencontrent lors de l’apprentissage de React.

Le fonctionnement de ‘setState()’

setState() est le seul moyen légitime de mettre à jour l’état après la configuration initiale de l’état. Disons que nous avons un composant de recherche et que nous voulons afficher le terme de recherche soumis par un utilisateur.

Voici la configuration:

import React, { Component } from 'react'class Search extends Component { constructor(props) { super(props) state = { searchTerm: '' } }}

Nous passons une chaîne vide comme valeur et, pour mettre à jour l’état de searchTerm, nous devons appeler setState().

setState({ searchTerm: event.target.value })

Ici, nous passons un objet à setState(). L’objet contient la partie de l’état que nous voulons mettre à jour qui, dans ce cas, est la valeur de searchTerm. React prend cette valeur et la fusionne dans l’objet qui en a besoin. C’est un peu comme si le composant Search demandait ce qu’il devait utiliser pour la valeur de searchTerm et setState() répondait avec une réponse.

Ceci lance fondamentalement un processus qui appelle la réconciliation par React. Le processus de réconciliation est la façon dont React met à jour le DOM, en apportant des modifications au composant en fonction du changement d’état. Lorsque la requête à setState() est déclenchée, React crée une nouvelle arborescence contenant les éléments réactifs du composant (ainsi que l’état mis à jour). Cet arbre est utilisé pour comprendre comment l’interface utilisateur du composant Search devrait changer en réponse au changement d’état en le comparant aux éléments de l’arbre précédent. React sait quelles modifications implémenter et ne mettra à jour les parties du DOM que si nécessaire. C’est pourquoi React est rapide.

Cela semble beaucoup, mais pour résumer le flux:

  • Nous avons un composant de recherche qui affiche un terme de recherche
  • Ce terme de recherche est actuellement vide
  • L’utilisateur soumet un terme de recherche
  • Ce terme est capturé et stocké par setStatesous forme de valeur
  • La réconciliation a lieu et React remarque le changement de valeur
  • React demande au composant de recherche de mettre à jour la valeur et le terme de recherche est fusionné dans

Le processus de réconciliation ne change pas nécessairement l’arbre entier, sauf dans une situation où la racine de l’arbre est modifiée comme ceci:

// old<div> <Search /></div>// new<span> <Search /></span>

Toutes les balises <div> deviennent des balises <span> et l’arborescence entière des composants sera mise à jour en conséquence.

La règle empirique est de ne jamais muter directement l’état. Utilisez toujours setState() pour changer d’état. La modification directe de l’état, comme l’extrait ci-dessous, n’entraînera pas le rendu du composant.

// do not do thisthis.state = { searchTerm: event.target.value}

Passer une fonction à `setState()`

Pour démontrer cette idée plus loin, créons un compteur simple qui incrémente et décrémente au clic.

Voir le stylo setState de Kingsley Silas Chijioke (@kinsomicrote) sur CodePen.

Enregistrons le composant et définissons le balisage de l’interface utilisateur:

class App extends React.Component {state = { count: 0 }handleIncrement = () => { this.setState({ count: this.state.count + 1 })}handleDecrement = () => { this.setState({ count: this.state.count - 1 })} render() { return ( <div> <div> {this.state.count} </div> <button onClick={this.handleIncrement}>Increment by 1</button> <button onClick={this.handleDecrement}>Decrement by 1</button> </div> ) }}

À ce stade, le compteur incrémente ou décrémente simplement le compte de 1 à chaque clic.

Mais que se passe-t-il si nous voulions plutôt incrémenter ou décrémenter de 3? Nous pourrions essayer d’appeler setState()trois fois dans les fonctions handleDecrement et handleIncrement comme ceci:

handleIncrement = () => { this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 })}handleDecrement = () => { this.setState({ count: this.state.count - 1 }) this.setState({ count: this.state.count - 1 }) this.setState({ count: this.state.count - 1 })}

Si vous codez à la maison, vous pourriez être surpris de constater que cela ne fonctionne pas.

L’extrait de code ci-dessus est équivalent à :

Object.assign( {}, { count: this.state.count + 1 }, { count: this.state.count + 1 }, { count: this.state.count + 1 },)

Object.assign() est utilisé pour copier des données d’un objet source vers un objet cible. Si les données copiées de la source vers la cible ont toutes les mêmes clés, comme dans notre exemple, le dernier objet gagne. Voici une version plus simple du fonctionnement de Object.assign();

let count = 3const object = Object.assign({}, {count: count + 1}, {count: count + 2}, {count: count + 3});console.log(object);// output: Object { count: 6 }

Ainsi, au lieu que l’appel se produise trois fois, il n’arrive qu’une seule fois. Cela peut être corrigé en passant une fonction à setState(). Tout comme vous passez des objets à setState(), vous pouvez également passer des fonctions, et c’est le moyen de sortir de la situation ci-dessus.

Si nous éditons la fonction handleIncrement pour ressembler à ceci:

handleIncrement = () => { this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 }))}

wenous pouvons maintenant incrémenter le nombre trois fois en un clic.

Dans ce cas, au lieu de fusionner, React met en file d’attente les appels de fonction dans l’ordre dans lequel ils sont effectués et met à jour l’ensemble de l’état. Cela met à jour l’état du compte à 3 au lieu de 1.

Accédez à l’état précédent À l’aide de Updater

Lors de la création d’applications React, il arrive que vous souhaitiez calculer l’état en fonction de l’état précédent du composant. Vous ne pouvez pas toujours faire confiance à this.state pour conserver l’état correct immédiatement après avoir appelé setState(), car il est toujours égal à l’état rendu à l’écran.

Revenons à notre contre-exemple pour voir comment cela fonctionne. Disons que nous avons une fonction qui décrémente notre nombre de 1. Cette fonction ressemble à ceci:

changeCount = () => { this.setState({ count: this.state.count - 1})}

Ce que nous voulons, c’est la capacité de diminuer de 3. La fonction changeCount() est appelée trois fois dans une fonction qui gère l’événement de clic, comme ceci.

handleDecrement = () => { this.changeCount() this.changeCount() this.changeCount()}

Chaque fois que le bouton à décrémenter est cliqué, le nombre décrémente de 1 au lieu de 3. En effet, this.state.count n’est pas mis à jour tant que le composant n’a pas été rendu à nouveau. La solution consiste à utiliser un programme de mise à jour. Un programme de mise à jour vous permet d’accéder à l’état actuel et de l’utiliser immédiatement pour mettre à jour d’autres éléments. Ainsi, la fonction changeCount() ressemblera à ceci.

changeCount = () => { this.setState((prevState) => { return { count: prevState.count - 1} })}

Maintenant, nous ne dépendons pas du résultat de this.state. Les états de count sont construits les uns sur les autres afin que nous puissions accéder à l’état correct qui change à chaque appel à changeCount().

setState()doit être traité de manière asynchrone — en d’autres termes, ne vous attendez pas toujours à ce que l’état ait changé après avoir appelé setState().

Conclusion

Lorsque vous travaillez avec setState(), voici les principales choses que vous devez savoir:

  • La mise à jour vers un état de composant doit être effectuée en utilisant setState()
  • Vous pouvez passer un objet ou une fonction à setState()
  • Passez une fonction lorsque vous pouvez mettre à jour l’état plusieurs fois
  • Ne dépendez pas de cela.indiquez immédiatement après l’appel de setState() et utilisez plutôt la fonction de mise à jour.