• 12 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/23/24

Exploitez vos connaissances de useState et useEffect pour effectuer des calls API

Exploitez vos connaissances de useState et useEffect pour effectuer des calls API

Rafraîchissez vos connaissances de useState et useEffect

Souvenez-vous… 💭

Le state local est présent à l’intérieur d’un composant : ce composant peut être re-render autant de fois que l'on veut, mais les données seront préservées. Pour cela on utilise  useState  , un hook qui permet d'ajouter un state local dans un composant fonction.

useEffect  est également un hook, qui permet d'exécuter des actions après le render de nos composants, en choisissant à quel moment et à quelle fréquence cette action doit être exécutée, avec le tableau de dépendances.

Vous vous en doutez sûrement, nous allons les utiliser pour faire des calls API :

  • useEffect  nous permettra de déclencher le fetch;

  • useState  permettra de stocker le retour de l'API dans le  state  . 

Alors, mettons-nous au travail. 💪

Récupérez les données d'une API

Reprenez les bases des calls API

Les données sont au cœur d'une application. Qu'il s'agisse de données locales ou bien qu'elles soient récupérées depuis une API, elles viennent alimenter nos composants et nourrir les interactions avec les utilisateurs.

Bon, on en entend parler tout le temps, mais c'est quoi une API ?

Une API (Application Programming Interface) est littéralement une interface de programmation d’application : c'est un moyen de communication entre deux logiciels. Concrètement, pour nous, c'est ce qui nous permet de récupérer des données. Si vous voulez en savoir plus sur les API, je vous conseille l'excellent cours Adoptez les API REST pour vos projets web.

Mais pourquoi on n'a pas mis les données directement dans le front ? On l'a bien fait dans le premier cours et ça fonctionne !

Eh bien... oui, on aurait pu. Ici, on dispose de toutes les données. Mais dans les faits, ce ne sera pas toujours le cas, loin de là. Vous pouvez par exemple avoir besoin que votre contenu soit administré par une personne qui ne sait pas coder. Dans ce cas, vous pourriez utiliser un CMS (comme WordPress, Ghost, etc.), et récupérer le contenu.

Ou bien tout simplement, vous pouvez créer une application complexe qui requiert un système d'authentification, qui sauvegarde des données utilisateurs, etc. Dans ce cas, une application frontend ne suffit pas et doit être complémentaire avec l'application backend.

Mettons tout ça en application dès maintenant !

Pour notre projet pour l'agence Shiny, nous allons utiliser l'API que vous trouverez ici. Vous trouverez toutes les instructions nécessaires pour la faire tourner dans le  README.md  .  Je vous laisse un petit moment pour cloner le repo et lancer l'API en local. ⏱

... C'est bon, vous avez bien l'API qui tourne en local ? On va aller récupérer le contenu de nos questions sur l'API sur la routehttp://localhost:8000avec la méthode  fetch() .

fetch()  est la méthode native pour faire des calls API. Nous aurions très bien pu utiliser des outils tels que axios  , mais ici la méthode native a été privilégiée, pour vous éviter une nouvelle installation d'outil externe.

Développez le questionnaire avec les données

Pour nous atteler à l'utilisation de l'API, nous allons développer la page /survey  . Souvenez-vous, dans le chapitre précédent, nous avions créé des liens pour naviguer entre les questions, et rediriger l'utilisateur sur /results  quand il atteignait la dixième question. Eh bien, ici, nous allons continuer à développer cette page afin de récupérer les données depuis l'API. J’ai ajouté un peu de style supplémentaire pour y voir un peu plus clair (que vous pouvez récupérer sur le repo GitHub de ce cours, sur la branche P2C1-exercice).

L’API nous renvoie l’ensemble des questions sur l’endpointhttp://localhost:8000/survey  .

Hé, mais comment on sait ça ?

Bon, c'est facile pour moi parce que j'ai aussi écrit l'API que nous utilisons. Mais vous pouvez tout simplement utiliser la documentation de l'API. Ici, elle est accessible dans le fichier   README.md  .

On peut donc l’appeler, comme nous l’avons dit, dans notre useEffect  pour récupérer les questions. Si vous regardez la documentation, vous verrez que la route correspondant aux questions ( http://localhost:8000/survey) est une route GET  , et qu'elle ne requiert pas de paramètre : on pourra récupérer les données en faisantfetch('http://localhost:8000/survey') .

Ici, on a donc uniquement besoin d’appeler l’API à la première initialisation de notre composant, et on précise un tableau de dépendances vide dans notre fichier  :

useEffect(() => {
   fetch(`http://localhost:8000/survey`)
        .then((response) => response.json()
        .then(({ surveyData }) => console.log(surveyData))
        .catch((error) => console.log(error))
    )
}, [])

Et voilà : on a bien ce qu’on voulait ! 🤩

Les données de notre questionnaire s'affichent dans la console
Les données de notre questionnaire arrivent dans la console ✨

Ici, nous avons utilisé des  Promises  . Une autre syntaxe aurait été possible avec des async  / await  . Mais attention, il y a une petite subtilité avec useEffect  . C'est ce que vous verrez dans le screencast à la fin de ce chapitre.

Bon, ce n’est pas tout d’afficher le retour de notre API dans la console : on veut que ce soit visible dans notre application !

Pour cela, nous allons utiliser le state. À l’aide de useState  , on crée donc :

const [questions, setQuestions] = useState({})

questions  va nous permettre de stocker l’objet qui a été retourné par l’API. À partir de là, on peut exploiter questions  assez simplement en appelant :

setQuestions(surveyData)  .

Ici, vous avez pu voir dans votre console que surveyData  est un objet ayant pour clé des nombres. C’est très pratique pour s’assurer que les questions sont toujours ordonnées, et on peut tout simplement accéder à une question avec :

surveyData[questionNumber]

De la même manière, pour savoir s’il faut mettre un lien vers le numéro de question suivant, ou bien un lien vers les résultats, vous pouvez tout simplement vérifier ce que donne l’affirmation :

surveyData[questionNumberInt + 1] ?

Ce qui nous donne le code suivant :

function Survey() {
    const { questionNumber } = useParams()
    const questionNumberInt = parseInt(questionNumber)
    const prevQuestionNumber = questionNumberInt === 1 ? 1 : questionNumberInt - 1
    const nextQuestionNumber = questionNumberInt + 1
    const [surveyData, setSurveyData] = useState({})
 
    useEffect(() => {
        setDataLoading(true)
        fetch(`http://localhost:8000/survey`)
            .then((response) => response.json()
            .then(({ surveyData }) => console.log(surveyData))
            .catch((error) => console.log(error))
        )
    }, [])
 
    return (
        <SurveyContainer>
            <QuestionTitle>Question {questionNumber}</QuestionTitle>
            <QuestionContent>{surveyData[questionNumber]}   </QuestionContent>
            <LinkWrapper>
                <Link to={`/survey/${prevQuestionNumber}`}>Précédent</Link>
                {surveyData[questionNumberInt + 1] ? (
                    <Link to={`/survey/${nextQuestionNumber}`}>Suivant</Link>
                ) : (
                    <Link to="/results">Résultats</Link>
                )}
            </LinkWrapper>
        </SurveyContainer>
    )
}
 
export default Survey

Créez un state loading

C'est pas mal tout ça, n'est-ce pas ? Notre question s'affiche bien :

Votre application affiche bien la question souhaitée.
Votre application affiche bien la question souhaitée

Mais ça vient d'où ce petit moment de “blanc” ? Comment fait-on pour que ça ressemble plus aux sites professionnels ?

Eh bien, ça correspond tout simplement au temps entre lequel le composant est render (généré) et celui où lequel les données sont chargées. Effectivement, d’un point de vue UI (interface utilisateur), ce n’est pas idéal : l’utilisateur ne comprend pas que les données sont en train d’être chargées, et peut alors penser qu’il y a un problème sur l’application.

Une pratique très répandue consiste à mettre un petit loader pour signifier que les données vont bientôt s’afficher. On pourrait mettre un simple texte “Chargement...”, mais bon, on sait manier le CSS : autant s’amuser avec, non ?

Je vous propose de créer un simple Loader  en CSS, directement dans le fichier utils/Atoms.jsx  . Pour cela, on a également besoin d’importer keyframes  depuis la bibliothèque styled-components  . Ce qui nous donne :

import colors from './colors'
import styled, { keyframes } from 'styled-components'
 
const rotate = keyframes`
    from {
        transform: rotate(0deg);
    }
 
    to {
    transform: rotate(360deg);
    }
`
 
export const Loader = styled.div`
    padding: 10px;
    border: 6px solid ${colors.primary};
    border-bottom-color: transparent;
    border-radius: 22px;
    animation: ${rotate} 1s infinite linear;
    height: 0;
    width: 0;
`

On va maintenant utiliser le state pour afficher notre Loader  . Pour cela, on crée une variable isDataLoading  avec useState  :

const [isDataLoading, setDataLoading] = useState(false)

Dans le useEffect  , on vient modifier notre booléen :

useEffect(() => {
    setDataLoading(true)
    fetch(`http://localhost:8000/survey`)
    .then((response) => response.json())
    .then(({ surveyData }) => {
    setSurveyData(surveyData)
    setDataLoading(false)
    })
}, [])

... ce qui nous permet ainsi de conditionner le rendu de notre composant : le Loader  s’affiche tant que les données chargent, et une fois qu’on les a bien, le contenu de la question s’affiche à la place du Loader  .

<SurveyContainer>
    <QuestionTitle>Question {questionNumber}</QuestionTitle>
    {isDataLoading ? (
        <Loader />
    ) : (
        <QuestionContent>{surveyData[questionNumber]}</QuestionContent>
    )}
...
</SurveyContainer>

Yaaaaaaaay ! 🎉 On a bien notre contenu qui s’affiche comme on le souhaitait !!

Votre page questionnaire affiche un composant de chargement quand les données sont en train d'être chargées
Pas mal, ce petit loader ?

Maintenant que nous avons le comportement que nous souhaitons, profitons-en pour implémenter une syntaxe un peu plus moderne, et pour gérer les erreurs :

C'est le moment de mettre tout ça en pratique. 💪

Exercez-vous

Vous avez réussi à récupérer les données depuis le backend de l’agence Shiny : félicitations à vous ! À vous de jouer pour faire la même chose pour la page des profils freelances. Comme d’habitude, vous trouverez le code nécessaire pour commencer l’exercice sur la branche P2C1-begin .

Pour cela, vous allez devoir :

  • Récupérer les profils des freelances sur l’endpoint /freelances  de l’API. Vous pouvez utiliser la syntaxe async  / await  ou les .then  .

  • Utiliser le Loader  lorsque le contenu des profils freelances est en train de charger.

  • Afficher les données dans la page.

  • Afficher une erreur s'il y a eu un problème.

Vous trouverez la solution de l’exercice sur la branche P2C1-solution. 💡

En résumé

  • Les calls API peuvent s’effectuer simplement avec les hooks useEffect  et useState  : 

    • useEffect  permet de déclencher l’appel API ;

    • et useState  permet de stocker les données qui sont retournées.

  • Il est tout à fait possible d’utiliser des promesses ou des async  / await  pour effectuer des calls asynchrones en React.

  • En termes d’UI, il est possible de créer un state loading  pour afficher une animation de chargement pendant que les données sont en train de charger.

Je vous avais dit dans le premier cours que les hooks useState  et useEffect  étaient particulièrement utiles. Je ne vous avais pas menti : en plus de pouvoir créer des interactions locales, ils permettent même d'interagir avec des services extérieurs ! Si c’est pas la belle vie. 😎

Nous allons maintenant nous plonger un peu plus loin dans le monde des hooks avec useContext  , et en profiter pour découvrir le Contexte, qui va nous permettre de partager simplement nos données entre nos composants. Alors c’est partiii ! 🚀

Example of certificate of achievement
Example of certificate of achievement