L'écran générateur de quiz
Laisser le joueur choisir un thème, un niveau et un nombre de questions, sans jamais lui permettre un réglage impossible.
Objectifs de la séance
- étendre les types et la fonction
filtrerQuestionsavec la valeur spéciale"tous"; - créer un composant de choix (une « puce ») accessible aux lecteurs d'écran ;
- calculer le nombre de questions disponibles et désactiver les réglages impossibles ;
- naviguer avec des paramètres :
router.push({ pathname, params }); - comprendre que les paramètres d'une adresse sont toujours des chaînes de caractères.
Petite vérification
Avant de commencer, vérifiez si vous savez...
- cliquer sur « Jouer » dans le navigateur et lire l'adresse (route) affichée dans la barre d'adresse
- cliquer sur le bouton retour de la barre du haut et dire quel écran s'affiche
- dire dans quel fichier est défini le titre « Partie en cours »
📌 Réponse attendue
TP pour réfléchir et résoudre des problèmes
Votre mission : créer l'écran generateur où le joueur règle sa partie, puis y accéder depuis l'accueil. Pour
l'instant, l'écran partie ignore encore ces réglages : vous les exploiterez à la séance suivante.
Étape 1 — Des types pour les réglages
Dans src/types/quiz.ts, ajoutez les types du générateur : un thème précis ou « tous », un niveau précis
ou « tous », et l'ensemble des réglages d'une partie.
Ajouter "tous" à l'union permet de traiter « pas de filtre » avec le même type que les vrais choix, sans
variable supplémentaire du genre utiliserFiltre: boolean.
Étape 2 — Étendre la fonction de filtrage
La fonction filtrerQuestions du tutoriel 2 accepte maintenant "tous" pour le thème ou le niveau.
Remplacez aussi, dans la signature, les types CodeTheme et Niveau par FiltreTheme et FiltreNiveau, et
questions: Question[] par questions: readonly Question[] (le tableau n'est pas modifié).
Mélanger || et && sans parenthèses donne souvent un résultat inattendu : && est prioritaire sur ||. Avec
des parenthèses, la lecture est évidente.
Étape 3 — Tester le filtrage avec « tous »
Dans tests/filtrerQuestions.test.ts, ajoutez un test. Il utilise des chiffres que vous connaissez : 27 questions
au total, 9 par thème, 9 par niveau.
Lancez npm test : tests 30 et pass 30.
Un test qui utilise des valeurs que l'on peut vérifier à la main (27, 9, 9) est facile à relire et à croire.
Étape 4 — Les constantes du générateur
Créez src/constants/generateur.ts : la liste des nombres de questions proposés et celle des niveaux avec leur libellé.
Au-dessus, ajoutez la ligne export const NOMBRES_QUESTIONS = [3, 5, 10]; et l'importation du type
(import type { FiltreNiveau } from "@/types/quiz";).
Les valeurs de réglage sont regroupées dans un fichier de constantes : pour proposer 20 questions, on modifie une ligne à un seul endroit, pas plusieurs écrans.
Étape 5 — Un composant de choix accessible
Créez src/components/ChoixOption.tsx : une « puce » qui affiche un libellé et indique si elle est sélectionnée.
Un utilisateur aveugle ne voit pas la couleur de la puce. accessibilityState lui permet d'entendre « sélectionné »
ou « désactivé ». Cela coûte une ligne et rend l'application utilisable par plus de monde.
Étape 6 — L'écran générateur : états et nombre disponible
Créez src/app/generateur.tsx. Trois useState mémorisent les réglages. Le nombre de questions
disponibles n'est pas un état : il se calcule à partir du thème et du niveau.
C'est la règle vue à la séance sur le score. Si disponibles était un état, il faudrait penser à le mettre à jour
à chaque changement de thème ou de niveau, et l'oublier un jour.
Étape 7 — Naviguer avec des paramètres
Au clic sur « Générer le quiz », on ouvre l'écran partie en lui transmettant les réglages dans l'adresse
(par exemple /partie?theme=reseaux&niveau=2&nombre=3).
Le bouton est désactivé quand les réglages sont invalides, mais la fonction vérifie quand même
if (!reglagesValides) return;. L'interface n'est jamais l'unique barrière.
Étape 8 — Désactiver les réglages impossibles
Parmi les nombres proposés (3, 5, 10), ceux qui dépassent le nombre de questions disponibles doivent être grisés. Complétez la puce correspondante.
Essayez : thème « Réseaux et Wi-Fi » et niveau « Expert » : il n'y a que 3 questions, les puces 5 et 10 sont grisées. Vous pouvez même obtenir un message d'erreur et un bouton désactivé si le nombre sélectionné (5 par défaut) est devenu trop grand.
Il vaut mieux empêcher une erreur que l'expliquer après coup. Le message « Pas assez de questions » n'est que le filet de sécurité, pour le cas où le nombre déjà sélectionné devient trop grand après un changement de thème.
Étape 9 — Y accéder depuis l'accueil
Dans src/app/index.tsx, le bouton ouvre maintenant le générateur. Dans _layout.tsx, ajoutez
<Stack.Screen name="generateur" options={{ title: "Générateur de quiz" }} /> entre l'accueil et la partie.
Testez le parcours complet : accueil, générateur, puis « Générer le quiz ». Regardez l'adresse dans la barre
du navigateur : elle contient vos réglages (?theme=...&niveau=...&nombre=...).
Étape 10 — Enregistrer votre travail
Le message décrit ce que l'utilisateur peut faire désormais, pas les fichiers modifiés.
📌 Une solution
Notions théoriques
Une valeur spéciale : « tous »
Le thème d'une question est l'un des trois codes ("mots-de-passe", "hameconnage", "reseaux"). Pour le
générateur, il faut un quatrième choix : tous les thèmes. On l'ajoute à l'union :
export type FiltreTheme = CodeTheme | "tous";
La fonction filtrerQuestions traite alors "tous" comme « ne pas filtrer sur ce critère » :
(theme === "tous" || question.theme === theme) && (niveau === "tous" || question.niveau === niveau)
Lisez-la ainsi : « le thème convient si l'on accepte tous les thèmes ou si c'est exactement celui demandé ; et le niveau convient de la même façon ».
On aurait pu utiliser null pour « tous ». La chaîne "tous" a l'avantage de s'écrire telle quelle dans une
adresse (?theme=tous) et de se lire facilement dans le code. Plus tard, quand les questions viendront de la base
de données, on traduira "tous" en null à la frontière entre l'application et la base.
Un composant de choix : la « puce »
ChoixOption reçoit un libelle, un booléen estSelectionne, et une prop facultative desactive. Son style
est un tableau de styles, dont certains sont conditionnels :
style={[
styles.puce,
estSelectionne && styles.selectionnee,
desactive && styles.desactivee,
]}
Si la condition est fausse, false est ignoré par React Native. Le style de base puce est toujours appliqué ;
selectionnee et desactivee s'y ajoutent quand c'est utile.
Calculer le nombre disponible, bloquer les réglages impossibles
Le joueur peut combiner thème, niveau et nombre. Certaines combinaisons sont impossibles : on ne peut pas demander 10 questions quand il n'y en a que 3. Trois protections :
- les puces trop grandes sont désactivées (
desactive={n > disponibles}) ; - un message explique le problème si le nombre déjà choisi devient trop grand ;
- le bouton « Générer le quiz » est désactivé, et la fonction
genererLeQuizrefuse de continuer sireglagesValidesest faux.
Naviguer avec des paramètres
router.push({
pathname: "/partie",
params: { theme, niveau: String(niveau), nombre: String(nombre) },
});
L'adresse obtenue est /partie?theme=reseaux&niveau=2&nombre=3. Après le point d'interrogation figurent les
paramètres (clé=valeur, séparés par &).
Dans une adresse, tout est texte : nombre=3 est la chaîne "3", pas le nombre 3. C'est pourquoi on écrit
String(niveau) et String(nombre). Et surtout : n'importe qui peut modifier l'adresse à la main (nombre=999,
theme=pirates). Le prochain tutoriel vous apprend à les vérifier avant de les utiliser.
Test de mémorisation/compréhension
Exercices complémentaires
Exercice A : proposer 20 questions
Ajoutez 20 à NOMBRES_QUESTIONS dans src/constants/generateur.ts. Que se passe-t-il pour la puce « 20 » avec le thème « Tous » et le niveau « Tous » (27 questions) ? Et avec un thème précis ?
Exercice B : un test supplémentaire
Dans tests/filtrerQuestions.test.ts, ajoutez un test qui vérifie que filtrerQuestions(QUESTIONS_LOCALES, "mots-de-passe", 1) renvoie 3 questions (3 thèmes, 3 niveaux, 3 questions chacun).
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
"tous" | Valeur spéciale ajoutée à l'union des thèmes et des niveaux : « pas de filtre ». |
| Valeur calculée | Le nombre de questions disponibles se calcule, il ne se stocke pas. |
| Réglages impossibles | Puces grisées, message d'erreur, bouton désactivé et garde-fou dans la fonction. |
router.push({ pathname, params }) | Navigue vers une route en lui transmettant des paramètres dans l'adresse. |
| Paramètres d'URL | Toujours des chaînes de caractères, et modifiables par n'importe qui. |
accessibilityState | Informe les lecteurs d'écran de l'état d'un composant. |
Aperçu de la prochaine séance
Pour l'instant, l'écran partie ne regarde pas les paramètres reçus : il tire toujours 5 questions. Dans la
prochaine séance, vous allez lire ces paramètres avec useLocalSearchParams, puis surtout les valider,
car n'importe qui peut taper ?nombre=999 ou ?theme=pirates dans la barre d'adresse.