Personnaliser les composants avec les props
Qu'est ce qu'un prop?
Un
prop (abréviation de property) en React Native est une information transmise à un composant pour le personnaliser ou lui donner un comportement spécifique. C’est un peu comme les paramètres d’une fonction qui implémente le composant et que l'on appelle en lui passant les valeurs que l'on veut afin de la personnaliser (et personnaliser ainsi le composant qui en découle). En d'autres termes, un prop est une donnée que l’on passe à un composant React Native pour qu’il puisse l’utiliser dans son affichage ou sa logique.
Certains développeurs préfèrent utiliser le terme props pour le singulier plutôt que prop. Bien que ce ne soit pas syntaxiquement trop correct, elle reste quand même une pratique courante.
Personnaliser les composants réutilisables avec les props
Supposons que l'on veut importer nos boutons (de la leçon précédente) mais avec des étiquettes différentes. Disons par exemple que cette fois on veut importer nos deux boutons avec les étiquettes "Accepter" et "Refuser" (au lieu de "Continuer" et "Annuler"). Plutôt que de renommer statiquement les attributs
title dans le composant
MyButton, nous allons les rendre dynamique de telle sorte que leurs valeurs soient spécifiées au moment de l'importation, et ce, grâce aux props!
Commençons par le code de
App.js:
import { StyleSheet, View, Text, Button } from 'react-native';
import MyButtons from './components/MyButtons';
export default function App() {
return (
<View style={styles.container}>
<MyButtons titles={["Accépter","Refuser"]}/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
}
});
Nous avons ajouté le prop
titles qui a comme valeur un tableau qui contient les deux valeurs "Accepter" et "Refuser" à notre composant
MyByttons.
N'oubliez pas les accolade de l'interpolation JSX!
Maintenant, voyons à quoi ressemble le code du composant
MyByttons:
import { Button, View } from 'react-native';
function MyButtons(props){
return(
<View style={{flexDirection:"row", gap:4}}>
<Button title={props.titles[0]} />
<Button title={props.titles[1]} />
</View>
)
}
export default MyButtons
Tout d'abord, il faut spécifier l'argument qui fait office des props à la fonction
MyButtons qui implémente le composant. Sans trop d'originalité, j'ai appelé cet argument
props!
Il faut savoir que cet argument est de type objet Javascript. Ses clés ne sont rien d'autres que les noms des props qu'on a spécifié lors de l'intégration du composant dans le fichier
App.js. Dans notre cas, il s'agit de la clé
titles. Autrement dit, pour accéder à ce prop on utilisera la syntaxe
props.titles. Mais figurez-vous qu'on a spécifié un tableau comme valeur, donc il faut aussi préciser l'indice de l'élément auquel on veut avoir accès.
Donc, il suffit de spécifier la valeur de l'attribut
title des boutons comme ceci:
<Button title={props.titles[0]} />
<Button title={props.titles[1]} />
Au monent de l'exécution on aura:
Aller plus loin avec les props
Les props ne permettent pas de transférer que des valeurs simple comme on l'a vu dans l'exemple précédent, mais aussi des states, des setters de states, des event handlers et bien d'autres choses.
Imaginons cette fois que l'on veut intégrer un composant
Text à notre vue et qui affichera le message "Vous avez accepté" si on presse le bouton "Accepter" et "Vous avez refusé" si on presse le bouton "Refuser". Bien entendu, il existe plusieurs méthodes pour réussir un tel comportement, mais l'idée est d'utiliser un state, son setter et l'event handler qui exécute le setter du state, tous déclarés dans le fichier
App.js, mais certains d'entre eux seront passés en tant que props au composant
MyButtons qui exécutera l'event handler en pressant les boutons.
Commençons par le code de
App.js:
import { useState } from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import MyButtons from './components/MyButtons';
export default function App() {
const [message,setMessage]=useState(null)
const handleMessage=(msg)=>{
setMessage(msg)
}
return (
<View style={styles.container}>
<Text>{message}</Text>
<MyButtons
titles={["Accépter","Refuser"]}
handler={handleMessage}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
}
});
Je ne vais pas m'arrêter sur la section concernant la création du state et de l'event handler, car on a déjà traité ces deux points plusieurs fois auparavant, mais arrêtons nous plutôt sur le composant
MyButtons:
<MyButtons
titles={["Accépter","Refuser"]}
handler={handleMessage}
/>
Cette fois, nous avons passé deux props:
titles de la dernière fois et
handler qui passe l'event handler
handleMessage. Donc tout est là pour pouvoir contrôler la valeur du state et par conséquent, le messge à afficher.
Le code du composant
MyButtons ressemblera à ceci:
import { useState } from 'react';
import { Button, View } from 'react-native';
function MyButtons(props){
console.log(props)
return(
<View style={{flexDirection:"row", gap:4}}>
<Button title={props.titles[0]}
onPress={()=>{props.handler("Vous avez accepté")}} />
<Button title={props.titles[1]}
onPress={()=>{props.handler("Vous avez refusé")}} />
</View>
)
}
export default MyButtons
Remarquez l'instruction
console.log(props) qui affiche toutes les props que nous avons envoyé à notre composant. Il s'agit seulement d'un moyen de débogage, donc vous pouvez la retirer si tout fonctionne bien.
Sur les deux boutons, on ajouté l'événement
onPress auquel on a associé l'event handler qu'on a reçu à traver le props:
onPress={()=>{props.handler("Message personnalisé")}}
Il faut bien faire attention à une chose, la fonction que l'on appellera suite à l'événement accepte un argument, donc le fait de faire ceci:
onPress={props.handler("Message personnalisé")}
exécutera la fonction au chargement de la vue, donc avant même de presser le bouton. Il s'agit là d'un phénomène connu lié aux callbacks que nous avons déjà traité dans le cours de Javascript. En tout cas, dans ce genre de situation, il faut exécuter la fonction en question à travers une fonction anonyme. Ainsi, elle sera appelé quand l'événement associé sera détecté.
Déstructurer les props
Afin de rendre le code plus lisible (code du composant
MyButtons), il est préférable de déstructurer l'objet
props au moment de son passage à la fonction qui implémente le composant. Donc, le code du composant ressemblera à ceci (après la déstructuration):
import { useState } from 'react';
import { Button, View } from 'react-native';
function MyButtons({titles,handler}){
return(
<View style={{flexDirection:"row", gap:4}}>
<Button title={titles[0]}
onPress={()=>{handler("Vous avez accepté")}} />
<Button title={titles[1]}
onPress={()=>{handler("Vous avez refusé")}} />
</View>
)
}
export default MyButtons
Nous avons déstructuré l'argument qui fait office de props et qui est un objet Javascript comme ceci:
function MyButtons({titles,handler}){
...
}
De cette façon, dans le reste du code on invoque le prop
titles par
titles au lieu de
props.titles, ce qui est plus léger et plus lisible.
Le prop children
En Reac Native (tout comme en React), le prop
children fait office du contenu d'un composant importé. En effet, on peut toujours se permettre d'insérer des éléments dans un composant importé, comme du texte, des images etc... Ces éléments-là sont accessibles via le prop
children.
Imaginons que l'on souhaite créer une vue un peu particulière. Disons par exemple que l'on veut lui appliquer un style particulier, mais en même temps, on veut que son contenu soit défini au moment de l'importation du composant qu'elle définit.
Cette fois, j'ai créé un autre composant nommé
components/MyView.js.
Le code de
App.js ressemblera à ceci:
import { StyleSheet, View, Button } from 'react-native';
import MyView from './components/MyView';
export default function App() {
return (
<View style={styles.container}>
<MyView>
<Button
title="Je suis l'enfant de MyView"
/>
</MyView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
}
});
Au moment de l'intégration du composant
MyView nous y avons inclus un bouton qui porte l'étiquette "Je suis l'enfant de MyView":
<MyView>
<Button title="Je suis l'enfant de MyView" />
</MyView>
Remarquez que cette fois le composant MyView dispose de ce qui ressemble à une balise ouvrante et fermante afin d'englober des éléments dedans.
Le code de
MyView.js aura cette allure:
import {StyleSheet, View , Text } from 'react-native';
function MyView({children}){
return(
<View style={myStyle.buttonStyle}>
{children}
</View>
)
}
const myStyle=StyleSheet.create({
buttonStyle:{
backgroundColor:"#000",
width:300,
height:300,
justifyContent:"center",
padding:20
}
})
export default MyView
L'élément
children dans ce cas fait référence à tout ce qui a été englobé dans le composant
MyView au moment de son intégration dans
App.js. Autrement dit, l'élément
children correspond au bouton comme ceci:
<Button title="Je suis l'enfant de MyView" />