<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Communication et données externes :: App Web</title><link>https://cegepmv.github.io/420-211/4-communication-externe/index.html</link><description>Nous avons découvert, dans le module précédent, comment React gère l’état avec useState et comment afficher dynamiquement du contenu avec le rendu conditionnel. Ces concepts nous ont certes permis de créer des composants plus interactifs et réactifs, mais useState n’est en réalité qu’un premier aperçu d’un ensemble d’outils très puissants fournis par React ; les hooks.
Qu’est-ce qu’un hook ? Un hook est une fonction spéciale fournie par React qui permet à un composant fonctionnel de gérer des fonctionnalités avancées, comme :</description><generator>Hugo</generator><language>fr-fr</language><atom:link href="https://cegepmv.github.io/420-211/4-communication-externe/index.xml" rel="self" type="application/rss+xml"/><item><title>Concepts fondamentaux</title><link>https://cegepmv.github.io/420-211/4-communication-externe/1-concepts-fondamentaux/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cegepmv.github.io/420-211/4-communication-externe/1-concepts-fondamentaux/index.html</guid><description>Communication et récupération de données Dans les sections précédentes, nous avons utilisé des données “en dur” (mock data) ou locales (généralement un fichier data.js dans le répertoire /assets/ de nos projets). Dans une application réelle, les données vivent sur un serveur distant.
Cycle de vie d’une donnée Imaginez le flux suivant :
Le Frontend (React) envoie une requête (ex: “Donne-moi le profil de l’utilisateur 42”). L’API (Backend) reçoit la requête, vérifie les permissions, et communique avec la Base de données. La Base de données exécute une opération (ex: SELECT * FROM users...). Le Serveur formate le résultat (souvent en JSON) et le renvoie au Frontend. React reçoit la donnée, met à jour son state, et déclenche un nouveau rendu. Les bases du protocole HTTP Pour communiquer, le client et le serveur utilisent le protocole HTTP. Chaque requête utilise une “méthode” qui indique l’intention de l’action :</description></item><item><title>useEffect()</title><link>https://cegepmv.github.io/420-211/4-communication-externe/2-side-effects/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cegepmv.github.io/420-211/4-communication-externe/2-side-effects/index.html</guid><description>Dans React, lorsqu’on souhaite récupérer des données externes (par exemple depuis une API), on ne doit pas le faire directement dans le corps du component, car celui-ci peut être réexécuté plusieurs fois.
La bonne pratique consiste à utiliser le hook useEffect, qui permet d’exécuter une action après le rendu initial du composant (et à chaque mise à jour si on le souhaite).
Exemple inadéquat : appel direct dans le component fetch("https://pokeapi.co/api/v2/pokemon/pikachu") .then(res => res.json()) .then(data => console.log(data)) // déclenché à chaque render ! Le hook useEffect permet d’exécuter une fonction après le rendu du composant, et d’en contrôler la fréquence d’exécution.</description></item><item><title>Gérer l'expérience utilisateur</title><link>https://cegepmv.github.io/420-211/4-communication-externe/3-ux/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cegepmv.github.io/420-211/4-communication-externe/3-ux/index.html</guid><description>useEffect et le chargement des données Comme vu précédemment, un appel API est un effet de bord. La bonne pratique serait donc d’utiliser le hook useEffect() dans cette situation.
Cependant, on ne peut pas mettre async directement sur la fonction de useEffect. On crée donc une fonction asynchrone à l’intérieur de l’effet.
Gérer l’expérience utilisateur (UX) : état loading Une API peut prendre 100ms ou 3 secondes pour répondre. Il est crucial d’informer l’utilisateur que quelque chose se passe.</description></item><item><title>Laboratoire Harry Potter</title><link>https://cegepmv.github.io/420-211/4-communication-externe/4-laboratoire1/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cegepmv.github.io/420-211/4-communication-externe/4-laboratoire1/index.html</guid><description>L’objectif de cet atelier est de transformer une application statique en une plateforme dynamique connectée à une API. Vous allez mettre en pratique la récupération de données asynchrones, la gestion des effets de bord avec useEffect, et l’optimisation de l’expérience utilisateur (UX).
L’API utilisée : https://potterapi-fedeperin.vercel.app/fr/books
Nous allons commencer par cette base : ​ App.jsx App.css index.css import { useState } from 'react' import './App.css' function App() { const [book, setBook] = useState({ number: 1, title: "Harry Potter à l'école des sorciers", originalTitle: "Harry Potter and the Sorcerer's Stone", releaseDate: "Jun 26, 1997", description: "Le jour de son anniversaire, Harry Potter découvre qu'il est le fils de deux sorciers célèbres, dont il a hérité de pouvoirs magiques. Il doit fréquenter une célèbre école de magie et de sorcellerie, où il se lie d'amitié avec deux jeunes qui deviendront ses compagnons d'aventure. Lors de sa première année à Poudlard, il découvre qu'un sorcier malveillant et puissant nommé Voldemort est à la recherche d'une pierre philosophale qui prolonge la vie de celui qui la possède.", pages: 223, cover: "https://raw.githubusercontent.com/fedeperin/potterapi/main/public/images/covers/1.png", index: 0 }) return ( &lt;main className="app-shell"> &lt;section className="book-browser"> &lt;div className="controls" aria-label="Book navigation"> &lt;button type="button" className="nav-button" > Left &lt;/button> &lt;button type="button" className="nav-button" > Right &lt;/button> &lt;/div> &lt;section className="card"> &lt;article className="book-details"> &lt;img className="book-cover" src={book.cover} alt={`Cover of ${book.title}`} /> &lt;div className="book-copy"> &lt;p className="book-tagline"> Tome {book.number} • {book.releaseDate} &lt;/p> &lt;h2>{book.title}&lt;/h2> &lt;p className="description">{book.description}&lt;/p> &lt;dl className="detail-grid"> &lt;div> &lt;dt>Titre original&lt;/dt> &lt;dd>{book.originalTitle}&lt;/dd> &lt;/div> &lt;div> &lt;dt>Pages&lt;/dt> &lt;dd>{book.pages}&lt;/dd> &lt;/div> &lt;div> &lt;dt>Collection&lt;/dt> &lt;dd>Harry Potter&lt;/dd> &lt;/div> &lt;/dl> &lt;/div> &lt;/article> &lt;/section> &lt;/section> &lt;/main> ) } export default App .app-shell { min-height: 100vh; display: grid; place-items: center; padding: 32px 20px; } .book-browser { width: min(100%, 980px); padding: 32px; border-radius: 28px; background: radial-gradient(circle at top left, rgba(255, 220, 134, 0.18), transparent 28%), radial-gradient(circle at right bottom, rgba(127, 100, 255, 0.16), transparent 24%), rgba(25, 16, 34, 0.92); border: 1px solid rgba(255, 232, 176, 0.16); box-shadow: 0 24px 80px rgba(10, 5, 18, 0.46); backdrop-filter: blur(14px); } .heading-group { max-width: 720px; } .eyebrow { margin: 0 0 12px; color: #ffd36e; font-size: 0.85rem; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase; } .lede { margin: 0; color: rgba(247, 236, 222, 0.78); } .controls { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 28px 0 24px; } .nav-button { min-width: 110px; padding: 12px 18px; border: none; border-radius: 999px; background: linear-gradient(135deg, #ffd36e, #ff9f68); color: #261428; font: inherit; font-weight: 800; cursor: pointer; transition: transform 160ms ease, box-shadow 160ms ease, opacity 160ms ease; box-shadow: 0 16px 34px rgba(255, 159, 104, 0.24); } .card { min-height: 460px; border-radius: 24px; padding: 24px; background: rgba(8, 5, 14, 0.6); border: 1px solid rgba(255, 255, 255, 0.08); } .status-view { min-height: 412px; display: grid; place-content: center; justify-items: center; gap: 14px; text-align: center; color: #fff5e9; } .spinner { width: 58px; height: 58px; border-radius: 50%; border: 5px solid rgba(255, 255, 255, 0.16); border-top-color: #ffd36e; animation: spin 0.85s linear infinite; } .error-message h2 { color: #ffbeb2; } .error-message p { max-width: 36ch; color: #ffe2dc; } .book-details { display: grid; grid-template-columns: minmax(220px, 300px) 1fr; gap: 28px; align-items: center; } .book-cover { width: 100%; display: block; border-radius: 22px; border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: 0 20px 48px rgba(0, 0, 0, 0.38); } .book-copy h2 { margin: 10px 0 18px; font-size: clamp(2rem, 4vw, 3.4rem); } .book-tagline { margin: 0; color: #ffd36e; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; } .description { margin-bottom: 22px; color: rgba(255, 245, 233, 0.82); } .detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; margin: 0; } .detail-grid div { padding: 16px; border-radius: 18px; background: rgba(255, 255, 255, 0.04); border: 1px solid rgba(255, 255, 255, 0.08); } .detail-grid dt { margin-bottom: 6px; color: #ffd36e; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; } .detail-grid dd { margin: 0; color: #fff5e9; } :root { font-family: Georgia, 'Times New Roman', serif; line-height: 1.5; font-weight: 400; color: #fff5e9; background: radial-gradient(circle at top, #4b3557 0%, #24172f 32%, #120b17 72%); font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } * { box-sizing: border-box; } html { min-height: 100%; } body { min-height: 100vh; margin: 0; } button, input, textarea, select { font: inherit; } img { max-width: 100%; } #root { min-height: 100vh; } h1, h2, p, dl { margin-top: 0; } h1, h2 { line-height: 1.05; color: #fff8ef; } h1 { margin-bottom: 14px; font-size: clamp(2.5rem, 6vw, 4.8rem); }</description></item></channel></rss>