Formation React Native & Expo — Évaluation finale
| Durée | 2 heures |
| Documents autorisés | Documentation officielle uniquement (reactnative.dev, docs.expo.dev) et application CineTrack |
| Rendu | Merci de fork le projet comme ça je pourrais accèder à vos rendus |
| Barème | /100 points (+5 bonus) |
Vous devez compléter BookShelf, une application de découverte de livres avec une liste de lecture à 3 statuts ("À lire", "En cours", "Terminé"). L'application est partiellement construite : la navigation, les couleurs et les données sont en place.
Votre travail : compléter les fonctionnalités marquées // TODO pour rendre l'application fonctionnelle et enrichie.
Chaque TODO est numéroté, localisé dans un fichier précis, et décrit ce qui est attendu. Les points sont indiqués pour chaque TODO.
npm install
npx expo startapp/ ← Navigation
├── (tabs)/
│ ├── _layout.js ✅ Complet
│ ├── index.js 📝 Utilise TODO 1, 2, 5, 8, 9
│ ├── search.js 📝 Contient TODO 6
│ └── favorites.js ✅ Complet (utilise le contexte)
├── detail/
│ └── [id].js 📝 Contient TODO 3, 11, 13 (bonus)
└── _layout.js ✅ Complet
components/
├── BookCard.js 📝 TODO 1
└── ReadingProgress.js 📝 TODO 13 (bonus)
services/
└── books.js 📝 TODO 4
hooks/
├── useBooks.js 📝 TODO 5
└── useSearch.js 📝 TODO 6
contexts/
└── ReadingListContext.js 📝 TODO 7, 10
constants/ ✅ Complet
data/books.json ✅ Données fournies
utils/ ✅ Complet
📄 Fichier : components/BookCard.js
Créer un composant BookCard qui affiche une carte de livre avec :
- L'image de couverture (prop
cover, affichée viaImageavec une URI) - Le titre du livre (prop
title) - L'auteur (prop
author) - La note (prop
rating, affichée avec★et la valeur numérique) - Un
Pressableenglobant avec feedback visuel (opacity) et unonPress - Styles via
StyleSheet.create()en utilisant les couleurs deconstants/colors.js
Props attendues : { title, author, cover, rating, onPress }
📄 Fichier : app/(tabs)/index.js
Afficher les livres regroupés par genre avec un défilement horizontal par section (style Netflix) :
- Regrouper les livres par genre avec
useMemo(format :[{ genre, data }]) - Utiliser un
ScrollViewvertical contenant les sections - Pour chaque section : un
<Text>titre du genre + une<FlatList horizontal> - Chaque
FlatList:horizontal,showsHorizontalScrollIndicator={false},keyExtractor,contentContainerStyle,ItemSeparatorComponent(gap 12) - Les
BookCarddans les listes horizontales ont une largeur fixe (ex: 160)
📄 Fichier : app/detail/[id].js
Compléter l'écran de détail :
- Récupérer le paramètre
idavecuseLocalSearchParams() - Trouver le livre correspondant dans les données
- Afficher : titre, auteur, description, couverture, note, nombre de pages
- Gérer le cas où le livre n'est pas trouvé (message d'erreur)
📄 Fichier : services/books.js
Compléter les 3 fonctions du service :
getPopularBooks(): retourne la liste des livres depuisdata/books.jsongetBookById(id): retourne un livre par son idsearchBooks(query): filtre les livres dont le titre ou l'auteur contient la query
Chaque fonction doit :
- Être
async - Simuler un délai réseau avec le
delay()fourni - Utiliser le pattern
try/catchavec gestion d'erreur
📄 Fichier : hooks/useBooks.js
Créer un hook useBooks qui :
- Gère 3 states :
books,loading,error - Appelle
getPopularBooks()du service au montage - Expose une fonction
refreshpour recharger les données - Retourne
{ books, loading, error, refresh }
📄 Fichier : hooks/useSearch.js
Compléter le hook useSearch :
- Le state
queryetsetQuerysont fournis - Implémenter le debounce à 300ms avec
setTimeout - La cleanup function doit annuler le timer
- Si query est vide, vider les résultats sans appeler le service
- Gérer
loadingpendant la recherche
📄 Fichier : contexts/ReadingListContext.js
Compléter le système de liste de lecture avec 3 statuts (to_read, reading, finished) :
Partie A — Le reducer (5 pts)
LOAD: remplace le state paraction.payloadADD_TO_LIST: ajoute{ book, status }(si pas déjà présent)UPDATE_STATUS: change le statut d'une entrée existanteREMOVE_FROM_LIST: retire un livre par son id
Partie B — Le Provider (5 pts)
useReduceravec le reducer et[]comme état initialaddToList(book, status),updateStatus(id, status),removeFromList(id)getBookStatus(id)→ retourne le statut ou nullisInList(id)→ retourne true/false
📄 Fichier : app/(tabs)/index.js
Ajouter un système de filtrage par genre sur l'écran d'accueil :
- Extraire dynamiquement les genres disponibles depuis la liste de livres (avec
useMemo) - Afficher des chips horizontaux (
ScrollViewhorizontal) permettant de sélectionner un genre - Ajouter un chip "Tous" pour réinitialiser le filtre
- Filtrer les sections en fonction du genre sélectionné
📄 Fichier : app/(tabs)/index.js
Ajouter un système de tri sur l'écran d'accueil :
- Proposer plusieurs options de tri : par défaut, note croissante, note décroissante, titre A-Z, titre Z-A
- Afficher les options sous forme de chips horizontaux
- Appliquer le tri sur les livres dans chaque section (compatible avec le filtre genre)
- Utiliser
useMemopour optimiser le calcul
📄 Fichier : contexts/ReadingListContext.js
Persister la liste de lecture avec AsyncStorage :
- Au montage du Provider, charger la liste sauvegardée (action
LOADdans le reducer) - À chaque changement de la liste (après chargement initial), sauvegarder dans
AsyncStorage - Gérer les erreurs silencieusement (ne pas bloquer l'app si le storage échoue)
📄 Fichier : app/detail/[id].js
Ajouter un sélecteur de statut sur l'écran de détail :
- Importer
useReadingListetREADING_STATUSESdepuiscontexts/ReadingListContext - Afficher 3 boutons : "📖 À lire", "📚 En cours", "✅ Terminé"
- Le bouton du statut actuel doit être visuellement distinct (couleur différente)
- Au press : si pas dans la liste →
addToList(book, status)/ si déjà →updateStatus(id, status) - Si dans la liste, afficher un bouton "Retirer de ma liste" →
removeFromList(id)
📄 Fichier : components/ReadingProgress.js + app/detail/[id].js
Créer un composant barre de progression basé sur le statut de lecture :
to_read→ 0%,reading→ 50%,finished→ 100%- Afficher un label, une barre colorée (rouge → orange → vert) et le pourcentage
- Intégrer le composant dans l'écran de détail sous le sélecteur de statut
| TODO | Description | Points |
|---|---|---|
| 1 | BookCard | 12 |
| 2 | Sections horizontales par genre | 10 |
| 3 | Navigation détail | 8 |
| 4 | Service API | 12 |
| 5 | Custom Hook useBooks | 12 |
| 6 | Hook useSearch + debounce | 12 |
| 7 | ReadingListContext (reducer + provider) | 10 |
| 8 | Filtres par genre | 5 |
| 9 | Tri des livres | 5 |
| 10 | Persistance AsyncStorage | 6 |
| 11 | Sélecteur de statut de lecture | 8 |
| Total | 100 | |
| 13 | BONUS : ReadingProgress | +5 |
- Code propre et lisible (nommage, indentation)
- Gestion complète des cas d'erreur
- Utilisation systématique de
StyleSheet.create() - Respect de l'architecture 3 couches
- Livres similaires (même genre) affichés sur l'écran de détail
Bon courage ! 🚀