• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 23/02/2024

Allez plus loin avec les hooks

Si vous avez suivi le premier cours, vous avez découvert les hooks avec useState  et useEffect  . Ensemble, nous avons appris à les utiliser pour faire des calls API. Et dans le chapitre précédent, vous avez découvert le Contexte et comment y accéder simplement avec useContext  : vous commencez à avoir quelques cartes en main pour utiliser les hooks dans vos applications React. 🎉

Et si je vous disais que vous pouvez créer vos propres hooks ?

Créez vos propres hooks pour simplifier votre code

Je ne sais pas ce que ça évoque pour vous, mais moi la première fois qu'on m'a dit que je pouvais créer mes propres hooks, ça m’a un peu fait paniquer. Je m’imaginais devoir travailler sur la codebase de React, et devoir créer un de ces fichiers immenses pour pouvoir faire tourner un hook “custom”.

Détrompez-vous, la création d’un hook “custom” est toute simple : il s’agit juste d’une fonction qui commence par “use”, qui extrait de la logique réutilisable et qui peut utiliser d’autres hooks.

Mettons en application ce que nous avons vu dès maintenant.

Créez un hook pour vos calls API

Dans le premier chapitre, vous avez appelé l’API du projet Shiny pour récupérer les profils des freelances, ainsi que les questions de notre questionnaire.

Si vous regardez votre code d’un peu plus près, vous aussi vous voyez une certaine répétition ? C’est normal. On va donc essayer de mutualiser tout ça avec un hook “personnalisé” !

Pour cela, on crée dans /utils  un nouveau dossier qu’on appelle /hooks  et dans lequel on crée un ficher index.jsx  .

On va créer notre hook qu'on base cette fois-ci sur la syntaxe async  / await  :

import { useState, useEffect } from 'react'

 

export function useFetch(url) {

const [data, setData] = useState({})

const [isLoading, setLoading] = useState(true)

 

useEffect(() => {

if (!url) return

async function fetchData() {

const response = await fetch(url)

const data = await response.json()

setData(data)

setLoading(false)

}

setLoading(true)

fetchData()

}, [url])

 

return { isLoading, data }

}

Le code est plutôt explicite, n’est-ce pas ? Pour notre nouveau hook, useFetch  , on lui passe en paramètre l’URL de l’API qu’on veut appeler. Il possède un state interne qui lui permet de stocker la data, et de savoir si la data est en train de charger avec isLoading  .

Dans useEffect  , le hook fait un return  vide si le paramètre de l’URL est vide, et commence par mettre isLoading  à true  . Il déclare la fonction asynchrone fetchData  qui permet de :

  • appeller fetch  ;

  • parser ce qui est retourné avec data.json()  ;

  • et changer l’état de isLoading  .

url  fait partie du tableau de dépendances du useEffect  , ce qui permettra de redéclencher le call en cas de changement d’URL passée en paramètre. Puis, on appelle notre fonction fetchData  .

Pour utiliser notre nouveau hook, modifions /Survey/index.jsx  . On commence par importer notre hook avec :

import { useFetch } from '../utils/hooks'

Puis on récupère notre data avec :

const { data, isLoading } = useFetch(`http://localhost:8000/survey`)

const { surveyData } = data

Je vous invite aussi à supprimer tout le contenu du useEffect  qui permettait d’effectuer le fetch  . J’adore supprimer du code inutile : c’est super satisfaisant, non ? 😍

On n’oublie pas de remplacer isDataLoading  par isLoading  qui sera plus générique ici, et de remplacer surveyData  par data  .

Est-ce que ça marche toujours ?

... Oh, on a une erreur qui empêche le code de tourner ! 😭

Pas de panique, c’est normal ! Pour faire la navigation, on a accédé au contenu de notre objet de questions en faisant data[questionNumber]  . Sauf qu’à l’initialisation, data  est un objet vide... Ce qui veut dire qu'avec :

const { surveyData } = data

surveyData  est undefined. JavaScript provoque donc une erreur pour data[questionNumber]  . Une des manières d’éviter l’erreur est donc de vérifier que surveyData  est défini avant de l'utiliser dans le composant :

<QuestionContent>

{surveyData && surveyData[questionNumber]}

</QuestionContent>

Et voilà, notre page fonctionne à nouveau comme avant, en utilisant notre hook useFetch  , ce qui a permis de supprimer pas mal de code répétitif ! 🎉

Ajoutez une gestion d’erreur

Mais que se passe-t-il quand l’API nous renvoie une erreur ? Notre application ne se comportera pas comme prévu, et l’utilisateur n’en sera même pas informé. Je vous propose d’intégrer une gestion d’erreur dans notre hook useFetch  afin d'afficher à l'écran qu'il y a eu un problème.

Pour cela, dans utils/hooks/index.jsx  , on peut créer un state pour error avec :

const [error, setError] = useState(false)

Puis, on ajoute un try  et un catch à notre cher hook useFetch  :

export function useFetch(url) {

const [data, setData] = useState({})

const [isLoading, setLoading] = useState(true)

const [error, setError] = useState(false)

 

useEffect(() => {

if (!url) return

setLoading(true)

async function fetchData() {

try {

const response = await fetch(url)

const data = await response.json()

setData(data)

} catch (err) {

console.log(gerr)

setError(true)

} finally {

setLoading(false)

}

}

fetchData()

}, [url])

return { isLoading, data, error }

}

Ce qui nous permet de passer error  à true  lorsqu'un problème est rencontré.

De la même manière, dans Survey.jsx  , on récupère maintenant error  :

const { data, isLoading, error } = useFetch(`http://localhost:8000/survey`)

Et on peut tout simplement ajouter au-dessus du return  :

if (error) {

return <span>Il y a un problème</span>

}

Et voilà, vous avez même géré les erreurs d’appel API ! 🎉

Nous allons en profiter pour utiliser useFetch  pour envoyer les réponses de l'utilisateur, et donc récupérer les résultats de notre API depuis la page /results  .

Je vous montre tout ça dans le screencast juste en dessous. 👇

C'est pas mal ce useFetch  , je vais pouvoir l'implémenter dans toutes les bases de code en production ?

Le hook useFetch  que nous avons codé ici est très pratique pour éviter de nous répéter… et pour pratiquer la création de hooks personnalisés. Mais dans les faits, ce code est un peu trop basique pour être utilisé en production. À la place, vous pouvez utiliser un outil bien plus robuste tel que TanStack Query pour React , qui vous permet d'utiliser des hooks pour faire vos requêtes et les mettre en cache dans vos applications.

Maîtrisez les hooks

Intégrez les règles des hooks

Je le rappelle ici : les hooks ont leurs propres règles d’utilisation.

  • Les hooks sont uniquement accessibles dans un composant fonction React. Donc ce n'est pas possible d’en utiliser dans un composant classe ou bien dans une simple fonction JavaScript.

  • Appelez les hooks au niveau racine de vos composants.

  • Attention au nommage de vos hooks personnalisés : même s’il ne s’agit pas vraiment d’une règle obligatoire, mais d’une convention, vos hooks personnalisés doivent commencer par use  pour que l’on sache en un coup d’œil qu’il s’agit d’un hook.

Pour l’instant, voici quelques hooks que vous pourriez être susceptible de croiser dans différentes codebases :

useRef

Je vous laisse regarder par vous-même sur la documentation si ça vous intéresse, mais même s’il existe plusieurs utilisations de  useRef  , ce hook est avant tout utilisé pour interagir avec des éléments du DOM.

useReducer

useReducer permet de mieux gérer votre state lorsqu’il comporte de nombreuses propriétés qui doivent être modifiées régulièrement.

useMemo et useCallback

Ces deux hooks nous permettent d’éviter de refaire des calculs coûteux pour nos performances. Vous pouvez préciser des valeurs pour lesquelles il faudra refaire les calculs uniquement si l’un des paramètres change, grâce à useMemo  et useCallback  .

Et il en existe encore d’autres… À l’heure où ce cours a été écrit, nous en sommes à la version 17 de React. Mais nous ne sommes pas à l’abri que d’autres hooks soient créés entretemps.

Exercez-vous

Maintenant que nous avons créé un hook personnalisé pour faire des calls API useFetch  , vous allez pouvoir l’utiliser pour les pages /Freelances  ainsi que /Results  .

Vous allez également devoir créer un hook personnalisé useTheme  qui récupère le Contexte du theme  et vous permet de récupérer le thème actuel (  dark  ou light  ).

À partir de là, vous pouvez intégrer les changements de style pour le thème, et intégrer la page /results  en fonction des maquettes si vous voulez vous challenger (ou récupérer le CSS sur la branche solution si vous préférez passer moins de temps sur le CSS).

Comme d’habitude, vous trouverez le début de l’exercice sur la branche P2C3-begin et la solution sur P2C3-solution  .

En résumé

  • Un hook personnalisé est une fonction qui commence par “use”, qui extrait de la logique réutilisable et qui peut utiliser d’autres hooks. 

  • Les règles des hooks s’appliquent aux hooks personnalisés, en plus de la convention de nommer son hook personnalisé avec  use...   .

  • Il existe d’autres hooks tels que useRef  , useReducer  , useMemo  , etc.

Bon, votre application commence vraiment à ressembler à quelque chose ! J’espère que cet aperçu un peu plus poussé des hooks vous a bien plu. Mais comment vous assurer de ne pas tout casser si vous faites des modifications dans 6 mois… ou un an, alors que vous ne vous souvenez plus comment votre code est implémenté ? Eh bien… par les tests. C'est ce que nous allons voir dans la prochaine partie. Mais avant cela, vous allez pouvoir tester vos connaissances dans un quiz. Alors bonne chance à vous, et on se retrouve dans quelques instants ! 💪

Exemple de certificat de réussite
Exemple de certificat de réussite