Afficher des images avec expo-image
Illustrer les questions avec des images locales, un champ facultatif et le composant expo-image, puis fusionner le tutoriel dans main.
Objectifs de la séance
- ajouter des images au projet et les charger avec
require(); - comprendre pourquoi
require()n'accepte pas un chemin calculé ; - afficher une image avec le composant
Imaged'expo-image (contentFit,transition) ; - ajouter un champ facultatif
image?au typeQuestion; - afficher un élément seulement si une condition est vraie (
&&) ; - fusionner la branche
feature/composantsdansmainpar une Pull Request.
Petite vérification
Avant de commencer, vérifiez si vous savez...
- lancer l'application dans le navigateur et lire le texte du badge affiché à droite du titre
- réduire la hauteur de la fenêtre du navigateur et vérifier que l'écran défile
- citer le composant qui empêche le contenu de passer sous la barre du bas du téléphone
📌 Réponse attendue
TP pour réfléchir et résoudre des problèmes
Les questions sont plus parlantes avec une illustration. Votre mission : afficher une image au-dessus de
la carte, uniquement pour les questions qui en possèdent une. Vous êtes toujours sur la branche
feature/composants.
Étape 1 — Ajouter les images au projet
Trois illustrations libres de droits vous sont fournies. Enregistrez chacune d'elles (clic droit,
Enregistrer l'image sous...) dans le dossier assets/images/questions/ du projet (à créer) :
cadenas.png | hamecon.png | wifi.png |
|---|---|---|
![]() | ![]() |
Vérifiez que les trois fichiers apparaissent bien dans l'explorateur de VS Code, dans
assets/images/questions/.
Le sous-dossier questions/ regroupe les illustrations des questions. Plus tard, d'autres types d'images
(icônes, logos) auront leur propre sous-dossier.
Étape 2 — Un champ facultatif dans le type Question
Toutes les questions n'ont pas d'illustration. Dans src/types/quiz.ts, ajoutez au type Question un champ
facultatif : le point d'interrogation après le nom signifie « peut être absent ».
Un champ facultatif ne casse aucune des 24 autres questions : TypeScript n'exige image nulle part. On
ajoute une information sans modifier l'existant.
Étape 3 — Associer chaque nom de fichier à son image
Créez le fichier src/data/imagesLocales.ts. Les images locales se chargent avec la fonction require(),
dont le chemin doit être écrit en toutes lettres : on ne peut pas le construire à partir d'un nom
variable. On rassemble donc les trois require() dans un « dictionnaire » qui associe un nom de fichier à
son image :
Ce fichier est la seule liste des images de l'application. Ajouter une image revient à ajouter une ligne ici, sans toucher aux composants.
Étape 4 — Le composant IllustrationQuestion
Créez src/components/IllustrationQuestion.tsx. Il reçoit le nom de l'image, retrouve l'image dans le
dictionnaire et l'affiche avec le composant Image du paquet expo-image (déjà installé par le modèle de
projet). Complétez d'abord le garde-fou : si le nom est inconnu, le composant n'affiche rien plutôt que de
faire planter l'application :
Terminez le composant par l'affichage de l'image et ses styles. Complétez la ligne qui règle l'ajustement de l'image dans son cadre :
Une image manquante est un défaut visuel, pas une raison d'arrêter le quiz. Le garde-fou renvoie null
(« rien à afficher ») et le reste de l'écran continue de fonctionner.
Étape 5 — Associer des images à trois questions
Dans src/data/questionsLocales.ts, ajoutez le champ image aux questions 1, 10 et 19 (une par
thème). Complétez la question 1, puis faites de même avec "hamecon.png" pour la question 10 et
"wifi.png" pour la question 19. Placez la ligne image juste avant reponses.
Le type string accepte n'importe quel texte : une faute de frappe ("cadnas.png") n'est pas détectée
par tsc. C'est pourquoi le composant prévoit le cas d'un nom inconnu.
Étape 6 — Afficher l'image seulement si elle existe
Dans src/app/index.tsx, importez IllustrationQuestion puis placez-le entre l'en-tête et la carte.
Comme toutes les questions n'ont pas d'image, on utilise l'opérateur && :
Le cadenas s'affiche au-dessus de la carte de la première question. Faites un essai : remplacez
temporairement "cadenas.png" par "inconnue.png" dans la question 1 : plus d'image, mais aucune erreur
(c'est le garde-fou de l'étape 4). Remettez ensuite "cadenas.png".
Chaque fois qu'une valeur dépend d'un nom ou d'un indice calculé, prévoyez le cas « introuvable ». Ce réflexe évite de nombreux plantages.
Étape 7 — Enregistrer votre travail
tsc et npm test doivent passer avant de proposer la branche à la fusion : une Pull Request qui casse les
tests fait perdre du temps au relecteur.
Étape 8 — Fusionner le tutoriel dans main
Le tutoriel est terminé : tout le travail de feature/composants doit entrer dans main.
- Sur GitHub, ouvrez une Pull Request pour la branche
feature/composants(bandeau Compare & pull request), titre : « Composants et styles de l'écran du quiz ». - Relisez l'onglet Files changed : vous devez y voir les composants, les images, les données et l'écran modifié (la palette a déjà été fusionnée), et aucun fichier inattendu.
- Cliquez sur Merge pull request, Confirm merge, puis Delete branch.
- Dans le terminal, récupérez la fusion et supprimez la branche locale :
Après la fusion, vous êtes sur main, à jour, sans branche inutile : l'état idéal pour démarrer le tutoriel
suivant sur une nouvelle branche.
📌 Une solution
Notions théoriques
Images locales et require()
Une image locale est un fichier du projet, intégré à l'application au moment de sa construction. On la
charge avec require() :
const image = require("@/assets/images/questions/cadenas.png");
Le chemin doit être écrit en toutes lettres : le bundler (l'outil qui assemble l'application) lit
le code avant son exécution pour y repérer les images. Une écriture comme
require("@/assets/images/" + nom) est donc impossible, puisque nom n'existe qu'à l'exécution.
La solution : un « dictionnaire » qui associe un nom à son require() :
export const IMAGES_LOCALES: Record<string, number> = {
"cadenas.png": require("@/assets/images/questions/cadenas.png"),
};
Record<string, number>décrit un objet dont toutes les clés sont des textes et toutes les valeurs des nombres (require()renvoie ici un numéro qui désigne l'image) ;- on retrouve une image avec
IMAGES_LOCALES["cadenas.png"], ou avec une variable :IMAGES_LOCALES[nomImage].
Le composant Image d'expo-image
expo-image fournit un composant Image plus complet que celui de React Native : mise en cache,
transitions, formats modernes.
| Propriété | Rôle |
|---|---|
source | l'image à afficher (ici le résultat du require()) |
style | taille, arrondi... : obligatoire de donner une largeur et une hauteur à une image |
contentFit | comment l'image occupe son cadre : "contain" (entière, sans déformation), "cover" (remplit en rognant) |
transition | durée (en millisecondes) du fondu d'apparition |
accessibilityIgnoresInvertColors | sur iOS, empêche l'inversion des couleurs de l'image en mode d'accessibilité |
Un champ facultatif : image?
Dans le type Question, image?: string signifie que le champ peut être absent. TypeScript le traite
comme string | undefined : avant de l'utiliser, il faut vérifier qu'il existe, sinon l'éditeur signale une
erreur.
Affichage conditionnel avec &&
En JSX, condition && <Composant /> affiche le composant seulement si la condition est vraie (si elle est
fausse, rien n'est affiché) :
{question.image !== undefined && (
<IllustrationQuestion nomImage={question.image} />
)}
Dans la parenthèse, TypeScript sait que question.image est un texte (et non undefined) : le test
vérifie donc à la fois la logique et le type.
Avec {nombre && <Composant />}, si nombre vaut 0, React affiche... le chiffre 0. Préférez une
comparaison explicite (nombre > 0 && ..., ou !== undefined comme ici).
Test de mémorisation/compréhension
Exercices complémentaires
Exercice A : remplir le cadre
Remplacez contentFit="contain" par contentFit="cover" et comparez le résultat. Quelle est la différence
visible sur la bordure de l'image ? Remettez ensuite "contain".
Exercice B : ajouter une quatrième image
Ajoutez au dictionnaire une image de votre choix (que vous avez le droit d'utiliser), par exemple
pare-feu.png, puis associez-la à une question de la banque. Quelles sont toutes les lignes à modifier ou à
ajouter ?
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
require() | Charge une image locale ; le chemin doit être écrit en toutes lettres (jamais calculé). |
| Dictionnaire d'images | Record<string, number> : associe un nom de fichier à son require(). |
expo-image | Composant Image avec contentFit, transition et mise en cache. |
image? | Champ facultatif : absent pour certaines questions (string | undefined). |
condition && <X /> | Affiche X seulement si la condition est vraie. |
| Garde-fou | Prévoir le cas « introuvable » (undefined) pour ne jamais faire planter l'écran. |
Aperçu de la prochaine séance
L'écran du quiz est complet, mais figé : un clic sur une réponse n'écrit encore qu'un message dans la
console. Dans le prochain tutoriel, vous allez donner vie à l'application avec l'état de React : le
bouton choisi se colore en vert ou en rouge, la correction s'affiche, on passe à la question suivante et
le score se calcule, avec useState, puis useReducer et useEffect pour un chronomètre.

