Aller au contenu principal

Laboratoire 2: Hook useState

Introduction

Dans ce chapitre, nous allons:

  • Créer notre premier composants via les hooks
  • Utiliser le système de listeners

Tout d'abord, créez un nouveau projet via la commande suivante:

npm create vite@latest laboratoire_2 -- --template react

Nous en aurons besoin plus tard, dès que vous nous aurons fini les explications à propos des hooks.

Les Hooks

Dans le laboratoire précédent, nous avons vu ce qu'était un composant sous forme de classe. Il était aussi dit que l'approche avec les classes n'était plus d'actualité pour les nouveaux projets. Depuis plusieurs années, l'équipe de React a développé les Hooks. Il s'agit de fonctions utilitaires qui nous permettent de reproduire le comportement des classes !

useState()

Le premier hook que nous allons utiliser est useState(). Ce hook va nous permettre de créer un simple état. Contrairement au système de classe où un composant avait un seul état qui était un objet, le système de hooks nous permet de créer un état composé de plusieurs variables.

Tout d'abord, regardons comment fonctionne ce premier hook. useState(initialState) prend un argument: la valeur initiale de l'état. La fonction renvoie un tableau à deux éléments [state, setter]. Le premier élément est la valeur de l'état et le second est la fonction permettant de modifier l'état. Vous verrez souvent cette structure de code:

const [state, setter] = useState(initState);

Ce code permet de déconstuire le tableau pour que chaque élément soit associé à une variable, ce qui est bien plus pratique.

Intéressons-nous maintenant au deuxième paramètre. Il s'agit de la fonction permettant de modifier l'état associé. En utilisant cette fonction, React sera au courant du changement de l'état et pourra appliquer les changements désirés (ce qui sera important quand nous verrons le second hook).

danger

Cette fonction devrait être votre seule moyen pour changer l'état. Il est interdit de changer directement la variable contenant l'état !

Cette fonction setter() peut être utilisée de deux façons différentes:

  • avec une valeur comme paramètre
  • avec une fonction comme paramètre. Cette fonction prendra l'ancienne valeur comme paramètre et renverra le nouvel état.

La différence entre les deux approches est le fait que la première ne tient absolument pas compte de l'état précédent alors que le second oui ! Prenons un exemple:

function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}

function handleClick2() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}

Source

Vous opterez pour la première solution si le nouvel état ne se base pas sur le précédent. Dans les autres cas, vous opterez pour la seconde !

Prenons deux exemples qui viennent de la documentation officielle. Dans ce premier exemple, on se rend compte que le bouton "+3" ne fonctionne pas. En effet, la fonction increment() utilise la fonction, mais ne se base pas sur l'état précédent !

Dans ce second exemple, la fonction increment() utilise la fonction d'update, mais en utilisant l'état précédent. Comme nous pouvons le voir, le bouton "+3" fonctionne correctement cette fois-ci !

attention

Il est important de noter que quand un composant est re-rendu, ses enfants le sont aussi ! Par exemple:

<Composant1>
<Composant2/>
</Composant1>

Si Composant1 devait faire un nouveau rendu (à cause d'un changement d'état), alors Composant2 serait re-rendu à son tour. Et si Composant2 a des enfants, alors ses enfants seraient aussi re-rendus, etc...

Et les autres hooks ?

Il existe d'autres hooks dans React. La plupart sont des versions spécifiques qui permettent une gestion plus fine du système de rendu. Exemple: éviter de recalculer une opération si les différents paramètres ne changent pas (useMemo()). Nous ne les aborderons pas durant ce cours et vous ne devriez pas en avoir besoin. En effet, ces hooks servent à optimiser l'interface. Avec les deux hooks que nous verrons, vous devriez être capables de faire presque tout ce que vous désirez !

Femme avec des cheveux bruns, regardant avec une expression sérieuse.

Dans un prochain laboratoire, nous verrons le hook useEffect() qui sera utile pour charger les données depuis une API.

Mais puis-je changer l'état de mon composant en fonction des actions de l'utilisateur ?

Avec les listeners ! Souvenez-vous de JavaScript et ses listeners...

Winnie l'Ourson en train de réfléchir.

Ce que l'on souhaite est simple: à chaque modification d'un input, on modifie l'état associé, ce qui provoquera un nouveau rendu de notre composant ! Exemple:

./src/App.jsx
import "./App.css"
import {useState} from 'react';


function App() {
const [name, setName] = useState("")

return (
<div className="App">
<input onChange={event => setName(event.target.value)}/>
<p>Le nom que vous tapez est: {name}</p>
</div>
)
}

export default App

Un autre listener intéressant est onClick. Comme son nom l'indique, cet évènement s'enclenchera dès que vous effectuerez un clic.

Un homme en costume à carreaux présente un mélangeur, avec des objets sur une table et un fond en bois.

Exercices

Exercice 1

Nous allons faire un premier exercice pour voir si tout est bien compris. Nous allons créer un composant qui va afficher une liste de produits. La page sera composée de deux champs: un pour le nom du produit et un deuxième pour le prix. Vous aurez également un bouton valider le produit. Vous nommerez votre composant ListeProduits.

./src/ListeProduits.jsx
import {useState} from 'react';

function ListeProduits(){

// TODO: gérer l'état

return (
<>
<label>Nom: </label>
<input type={'text'} onChange={/*TODO*/}/>
<br/>
<label>Prix: </label>
<input type={'number'} onChange={/*TODO*/}/>
<br/>
<button onClick={/*TODO*/}>Ajouter</button>
<p>La liste des produits est:</p>
{
produits.length === 0 ? <p>vide</p> : (
<ul>
{produits.map(p => {
/*Indice: <li>{nom du produit} est vendu à {prix du produit} euro(s)</li>*/
})}
</ul>
)
}
</>
);
}

export default ListeProduits;
astuce

Vous allez devoir gérer une liste, donc n'oubliez pas l'attribut key pour les éléments de votre liste !

Pour tester, vous aurez besoin d'appeler votre nouveau composant dans App. Si vous faites appel à votre composant avant de l'avoir créé, vous aurez une erreur. Vous pouvez copier/coller le code suivant:

./src/App.jsx
import "./App.css"
import ListeProduits from './ListeProduits.jsx';


function App() {

return (
<div className="App">
<ListeProduits/>
</div>
)
}

export default App

Exercice 2

Dans ce deuxième exercice, vous devez rajouter un bouton à côté de la ligne pour la supprimer. Ne modifiez que le composant ListeProduits !

astuce

Pensez que l'état de votre composant n'est modifiable que via la fonction de mise à jour. Vous aurez sûrement besoin de l'opérateur spread (voir les ressources).

Exercice 3 (bonus)

Ce 3ème exercice est un exercice de dépassement. Actuellement, nous avons un problème de performance avec notre application ! A chaque fois que nous tapons un symbole dans un champ, le listener onChange s'enclenche... Donc si nous tapons le mot "pomme", nous aurons 5 rendus parfaitement inutiles. Même si React est "intelligent" et verra que le nouveau rendu ne provoque aucun changement au niveau de l'HTML (et donc aucun besoin de changer le DOM), nous avons cette étape de vérification inutile. Dans cet exercice, nous allons essayer de changer l'état uniquement lorsque l'on cliquera sur le bouton !

Vous ne pourrez pas utiliser le listener onChange. Par contre, vous aurez besoin d'un nouveau hook: useRef. Nous utiliserons ce hook pour faire des références vers des éléments HTML.

Etant donné qu'il s'agit d'un exercice de dépassement, aucune aide supplémentaire ne vous sera donnée !

Un homme en costume noir et blanc dit "Bonne chance".