Créer des composants avec props
Isoler la carte de question et le bouton de réponse dans des composants réutilisables, configurés par des props.
Objectifs de la séance
- créer deux composants,
CarteQuestionetBoutonReponse, chacun dans son fichier ; - leur transmettre des informations avec des props typées ;
- rendre une réponse cliquable avec
PressableetonPress; - changer l'apparence du bouton pendant l'appui (style sous forme de fonction) ;
- afficher un message dans la console avec
console.log.
Petite vérification
Avant de commencer, vérifiez si vous savez...
- dans
src/app/index.tsx, recopier l'expression JSX qui affiche le texte de la troisième réponse de la question - exécuter
git branchet lire la branche courante - dans
index.tsx, citer la propriété de style et sa valeur qui arrondit les coins de la carte
📌 Réponse attendue
TP pour réfléchir et résoudre des problèmes
Le code de l'écran d'accueil mélange tout : la carte, les réponses, les styles. Votre mission : en
extraire deux composants réutilisables, qui vivent dans le dossier src/components/. Vous êtes
toujours sur la branche feature/composants.
Étape 1 — Typer les props de CarteQuestion
Créez le dossier src/components/ et le fichier src/components/CarteQuestion.tsx. La carte reçoit
trois informations : le numéro de la question, le total de questions et l'énoncé. On les
décrit dans un type appelé Props :
Le type Props est le « mode d'emploi » du composant : TypeScript refuse de l'utiliser avec une prop
oubliée ou du mauvais type. C'est une documentation toujours à jour.
Étape 2 — Écrire le composant CarteQuestion
Sous le type, écrivez la fonction du composant. Elle reçoit ses props entre accolades (en les « déstructurant ») et renvoie le JSX de la carte :
Terminez le fichier avec les styles de la carte (déplacés depuis index.tsx) :
const styles = StyleSheet.create({
carte: {
backgroundColor: COULEURS.carte,
borderRadius: 12,
padding: 16,
marginBottom: 16,
},
numero: {
fontSize: 14,
color: COULEURS.texteSecondaire,
marginBottom: 8,
},
enonce: {
fontSize: 18,
fontWeight: "600",
color: COULEURS.texte,
},
});
Le nom du fichier (CarteQuestion.tsx) est le même que celui du composant exporté. On retrouve ainsi
n'importe quel composant sans le chercher.
Étape 3 — Un bouton cliquable : Pressable et onPress
Créez src/components/BoutonReponse.tsx. Un <Text> ne réagit pas au toucher : on l'entoure d'un
composant Pressable, auquel on transmet la fonction à exécuter lors de l'appui, dans l'attribut
onPress. Cette fonction est elle-même une prop du composant :
Le type () => void signifie « une fonction sans paramètre qui ne renvoie rien ».
BoutonReponse ne sait pas ce qui se passe quand on clique : il appelle la fonction qu'on lui a donnée.
C'est le parent qui décide de l'action, ce qui rend le bouton réutilisable partout.
Étape 4 — Changer l'apparence pendant l'appui
La ligne style={({ pressed }) => [...]} utilise une particularité de Pressable : son attribut
style peut être une fonction qui reçoit l'état du bouton. pressed vaut true tant que le doigt
est posé. Ajoutez les styles du bouton, en complétant celui de l'appui :
Dans le tableau [styles.bouton, pressed && styles.boutonAppuye], l'expression pressed && ... vaut
false quand le bouton n'est pas appuyé : React Native ignore alors cette valeur, et seul le style
de base s'applique.
Un bouton qui ne réagit pas visuellement donne l'impression de ne pas fonctionner. Un changement d'opacité, même discret, confirme à l'utilisateur que son geste a été pris en compte.
Étape 5 — Utiliser CarteQuestion dans l'écran
Dans src/app/index.tsx, importez les deux composants. Supprimez de l'écran la View « carte » et
son énoncé, ainsi que les styles carte, enonce et reponse devenus inutiles, puis utilisez le
nouveau composant :
Un composant ne modifie jamais ses props : il les lit seulement. Les données descendent du parent vers l'enfant, jamais l'inverse.
Étape 6 — Utiliser BoutonReponse et réagir au clic
Remplacez ensuite les quatre <Text> des réponses par quatre <BoutonReponse>. Pour vérifier que le
clic fonctionne, ajoutez dans Accueil une petite fonction qui écrit dans la console :
function afficherDansLaConsole(texte: string) {
console.log("Réponse choisie :", texte);
}
Puis complétez la première réponse :
Faites de même pour les indices 1, 2 et 3.
Écrire onPress={afficherDansLaConsole(texte)} exécute la fonction immédiatement, à l'affichage de
l'écran, et non au clic. Pour la retarder, il faut l'envelopper dans une fonction fléchée :
onPress={() => afficherDansLaConsole(texte)}.
afficherDansLaConsole décrit exactement son rôle. Plus tard, cette fonction sera remplacée par une vraie action (valider la réponse) : un nom explicite rend ce remplacement évident.
Étape 7 — Tester le clic
Cliquez sur une réponse dans le navigateur, puis ouvrez la console des outils de développement
(F12, onglet Console). Un message Réponse choisie : ... apparaît à chaque clic. Pendant
l'appui, le bouton devient semi-transparent. Sur un téléphone avec Expo Go, les messages s'affichent
dans le terminal où tourne npx expo start.
Un console.log est un outil de débogage provisoire. Dans la version finale d'une application, on le
retire (nous le ferons dans un tutoriel ultérieur, quand les clics auront une vraie action).
Étape 8 — Enregistrer votre travail
Si vous avez oublié une prop en utilisant un composant, npx tsc --noEmit vous le signale tout de
suite avec un message clair.
📌 Une solution
Notions théoriques
Qu'est-ce qu'un composant ?
Un composant est une fonction qui reçoit des informations et renvoie du JSX. On le choisit pour deux raisons :
- réutiliser : le même
BoutonReponseservira pour les quatre réponses de chaque question ; - découper : un écran de 200 lignes devient quelques composants de 30 lignes, bien plus faciles à lire.
Le nom d'un composant commence toujours par une majuscule (CarteQuestion). Sans majuscule,
React croirait à une balise ordinaire.
Les props : les paramètres d'un composant
Les props (properties) sont les informations qu'un composant reçoit de son parent. Elles s'écrivent comme des attributs, et le composant les récupère dans ses paramètres :
// Utilisation (dans le parent)
<CarteQuestion numero={1} total={5} enonce="Quelle est la bonne réponse ?" />
// Définition (dans CarteQuestion.tsx)
export default function CarteQuestion({ numero, total, enonce }: Props) { ... }
- les accolades dans les paramètres (
{ numero, total, enonce }) déstructurent l'objet de props ; - le type
Propsdécrit leur forme : une prop manquante ou du mauvais type est signalée par TypeScript ; - une valeur texte fixe peut s'écrire avec des guillemets (
enonce="..."), tout le reste (nombre, variable, fonction) s'écrit entre accolades ; - les props sont en lecture seule : un composant ne les modifie jamais.
Une responsabilité par composant
CarteQuestion affiche une question, BoutonReponse affiche un bouton : chacun fait une seule
chose. Le composant ne connaît ni la banque de questions ni ce que fera le clic ; l'écran, lui,
assemble les composants. Cette séparation facilitera l'ajout du score, du chronomètre ou de la
navigation.
Pressable et onPress
Pressable est le composant de base pour tout ce qui doit réagir au toucher. Son attribut onPress
reçoit la fonction à exécuter lors de l'appui :
| Écriture | Effet |
|---|---|
onPress={onPress} | on transmet la fonction reçue en prop |
onPress={() => afficherDansLaConsole(texte)} | on crée une fonction qui appellera afficherDansLaConsole au clic |
onPress={afficherDansLaConsole(texte)} | erreur : la fonction est exécutée tout de suite et son résultat transmis |
Le style d'un Pressable peut être une fonction qui reçoit { pressed } : on peut ainsi
changer l'apparence pendant l'appui. Un tableau de styles [a, b] les combine, le dernier l'emportant
en cas de conflit ; une valeur false dans le tableau est ignorée.
Test de mémorisation/compréhension
Exercices complémentaires
Exercice A : une prop facultative
Ajoutez à CarteQuestion une prop facultative theme?: string (le point d'interrogation la rend
facultative). Quand elle est fournie, affichez-la sous le numéro de la question dans un <Text>.
Vérifiez que l'écran fonctionne avec et sans cette prop.
Exercice B : un composant oublié
Dans index.tsx, supprimez la prop total de <CarteQuestion ... />. Que signale l'éditeur ? Que
répond npx tsc --noEmit ? Remettez la prop.
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
| Composant | Fonction au nom commençant par une majuscule, qui renvoie du JSX ; un composant par fichier. |
| Props | Informations reçues du parent, décrites par un type Props, en lecture seule. |
| Déstructuration | ({ numero, total }: Props) récupère directement chaque prop. |
Pressable / onPress | Rend un élément cliquable ; onPress reçoit la fonction à exécuter. |
() => ... | Fonction fléchée : retarde l'exécution jusqu'au clic. |
style={({ pressed }) => ...} | Style qui change pendant l'appui. |
Aperçu de la prochaine séance
Les quatre réponses sont encore écrites une par une. Que se passerait-il si une question avait cinq
réponses, ou trois ? Dans la prochaine séance, vous allez remplacer ces quatre blocs par une seule
instruction, map(), qui crée un bouton par réponse, et vous découvrirez pourquoi React
exige une clé unique pour chaque élément d'une liste.