Le hook : useState
Un Hook est une fonction qui permet d' « accrocher » une des fonctionnalités React à un composant (une fonction-composant).
Le "hook" useState est utilisé pour gérer le changement d'état du composant (ie : les données changent au fil du temps par exemple suite à une action de l'utilisateur).
Donc, pour ajouter un état à une fonction-composant, il faut l'importer :
import React, { useState } from 'react';
Ensuite, on déclare l'état de la fonction- composant par l'appel à useState() en utilisant la syntaxe suivante :
const [state, setState] = useState(initialState)
L'emploi de useState a trois conséquences :
création et initialisation d'une variable (dite variable d'état pour mémoriser l'état de la fonction-composant) : dans l'exemple ci-dessous, les variables d'état sont "studentName", "course" et "colore" ;
initialisation de la variable d'état avec la valeur initialState, paramètre de useState(). Dans l'exemple :
la variable d'état "colore" est initialisée à "blue" ;
la variable d'état "course" est initialisée avec une chaîne vide.
création d'un setter : fonction qui met à jour la valeur la variable d'état créée. Dans l'exemple, les setter sont "setStudentName", "setCourse" et "setColoreBorderInputText" ;
Exemple : la fonction-composant Crud()
const Crud = () => {
//déclaration de l'état du composant
const [ studentName, setStudentName ]= useState("");
const [ course, setCourse ]= useState("");
const [ colore, setColoreBorderInputText ]= useState("blue");
// ...
return(
<>
<View style={styles.container, {backgroundColor:"lightcyan"}}>
{ Lire()}
</View>
<View style={styles.container , {backgroundColor:"lightcyan"}}>
{Inscrire()}
</View>
</>
)
}
mettra à jour sa variable d'état (dans l'exemple : studentName sera mis à jour) ;
si la mise à jour modifie la valeur de la variable, cela provoquera un nouveau rendu ("re-chargement") de la fonction- composant.
React se rappelle de la valeur des variables d'état entre deux rendus et fournit toujours la plus récente à notre fonction-composant.
L'image ci-dessus est une illustration de l'erreur produite par la fonction-composant "Compteur" dont le code est affiché ci-dessous.
Le problème : le setter setCount() est invoquée lorsque le composant est rendu, il met à jour l'état, ce qui provoque un nouveau rendu. Celui-ci invoque donc le setter qui met à jour l'état, et qui provoque un nouveau rendu et cela boucle à l'infini.
Exemple de boucle infinie :
const Compteur = () => {
const[ count, setCount] = useState(0);
setCount((count) => count + 1);
return (
<View>
<Text
style={{
justifyContent: 'center',
alignItems: 'center',
marginTop: 16,
}}
>
Compteur: {count}
</Text>
</View>
)
};
Dans l'exemple ci-dessous, il n'y plus de boucle infinie car le setter n'est appelé que si une condition est remplie.
const Compteur = () => {
const[ count, setCount] = useState(0);
let i = Math.random();
console.log("re-render: " + i);
if (i < 0.9) {
console.log("un de plus");
setCount (count + 1);
};
return (
<View>
<Text
style={{
justifyContent: 'center',
alignItems: 'center',
marginTop: 16,
}}
>
Compteur: {count}
</Text>
</View>
)
};
L'illustration ci-jointe montre que 4 rendus se sont produits :
le 1er au chargement de la page (la console affiche :"re-render : 0.74") puis
parce que 0.74 < 0.9, le test i<0.9 rend "vrai", la console affiche "un de plus", il y appel à setCount avec 1 pour paramètre. La variable d'état count est mise à jour (sa valeur est maintenant 1) et il y a un nouveau rendu pour lequel la console affiche : "re-render : 0.88" ; puis
parce que 0.88 < 0.9, le test i<0.9 rend "vrai", la console affiche "un de plus", il y appel à setCount avec 2 pour paramètre. La variable d'état count est mise à jour (sa valeur est maintenant 2) et il y a un nouveau rendu pour lequel la console affiche : "re-render : 0.44" ; puis
parce que 0.44 <0.9, le test i<0.9 rend "vrai", la console affiche "un de plus", il y appel à setCount avec 3 pour paramètre. La variable d'état count est mise à jour (sa valeur est maintenant 3) et il y a un nouveau rendu pour lequel la console affiche : "re-render : 0.95" ; puis
puisque 0.95 >0.9, le test i<0.9 rend "faux" et la fonction-composant peut rendre son résultat : count = 3
Ci- dessous figure le code d'un compteur qui s'incrémente toutes les 5s (plus précisément devrait ...)
const Gps = () => {
const[ inter, setInter] = useState(5000);
const[ count, setCount] = useState(0);
const compteur = (interval) => {
console.log("debut execution compteur");
// toutes les 5s, setInterval exécute setCount pour incrémenter notre compteur
const ref = setInterval(function () {
setCount((count) => count + 1);
}, interval);
console.log("ref : " + ref + " compteur : " + count);
return ;
}
console.log(" le rendu principal");
compteur(inter);
return (
<View>
<Text>
Intervalle : {inter} et Compteur : {count} !
</Text>
</View>
);
}
L'illustration ci-dessous montre la boucle infinie de rendu
le 1er au chargement de la page l'appel à setInterval rends la référence 16 avec un compteur à 0 puis
suite l'appel à setCount un nouveau rendu se produit avec un nouvel appel à setInterval (ref : 20), le compteur est bien incrémenté
suite l'appel à setCount qui un nouveau rendu avec un nouvel appel à setInterval (ref : 22), le compteur est bien incrémenté
suite l'appel à setCount qui un nouveau rendu avec un nouvel appel à setInterval (ref : 24), le compteur est bien incrémenté
...
Notre compteur est bien incrémenté mais l'intervalle entre chaque incrémentation n'est plus respecté, notre compteur s'emballe.
Le hook useEffect, présenté dans ce billet, va nous aider à résoudre ce problème.
Une fonction "Lire()", présentée ici, permet d'afficher les données actuellement stockées dans une table d'une base de données.
Une fonction "Inscrire()", décrite ci-dessous permet la saisie d'un nouvel enregistrement dans cette table.
Les contrôles "TextInput" utilisent les 3 setters pour :
mémoriser un nom d'étudiant ;
mémoriser un cours ;
mémoriser une couleur.
const Inscrire =() => {
return(
<View>
<View style={{width:"100%", backgroundColor:"coral",}}>
<Text style={{ textAlign:"center", fontWeight :"bold",fontSize:20 }}>
- Ajouter un enregistrement -
</Text>
</View>
<TextInput
style= { [styles.input, {borderColor:colore,width:220} ]}
placeholder={"nom de l'étudiant : "}
onChangeText={setStudentName}
value={studentName}
maxLength={24}
onFocus={colore => setColoreBorderInputText("red")}
/>
<TextInput
style= { [styles.input, {borderColor:colore,width:220} ]}
placeholder={"nom du cours : "}
onChangeText={setCourse}
value={course}
maxLength={24}
onFocus={colore => setColoreBorderInputText("red")}
/>
<View style= { {width:"50%", alignSelf:"center"} }>
<Button
style= {styles.button }
title={"Enregistrer"}
onPress={InsertRecord}
/>
</View>
</View>
)}
}