Aller au contenu principal


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)

Billet créé le :
31 mai 2022
Dernière MAJ :
04 Mar 2024

L'emploi de useState a trois conséquences :

  1. 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" ;

  2. 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.

  3. 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>
    </>
    )
}

 

Attention cependant, chaque utilisation d'un setter (exemple : setStudentName)
  • 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.

information complémentaire disponible en cliquant iciUne erreur assez classique : la boucle infinie de rendu...

Cliquer pour agrandir l'image

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

Cliquez pour agrandir l'image

information complémentaire disponible en cliquant iciUseEffect pour gérer la boucle infinie de rendu...

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.

Cliquez pour agrandir l'image

 Le hook "useRef" apporte également une réponse à 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>
    )}
}