Composants Primitifs
En React web, vous utilisez des balises HTML (<div>, <p>, <img>). En React Native, ces balises n'existent pas. Vous utilisez des composants primitifs fournis par React Native qui sont traduits en composants natifs de chaque plateforme.
Du HTML aux composants React Native
Voici la correspondance entre les balises HTML et les composants React Native :
| HTML (React web) | React Native | Usage |
|---|---|---|
<div> | <View> | Conteneur, mise en page |
<p>, <h1>, <span> | <Text> | Afficher du texte |
<img> | <Image> | Afficher une image |
<div style="overflow: scroll"> | <ScrollView> | Zone défilable |
<input> | <TextInput> | Saisie de texte |
<button> | <Pressable> ou <Button> | Élément cliquable |
| (pas d'équivalent) | <SafeAreaView> | Éviter l'encoche et la barre d'état |
Tous les imports sont explicites
En React Native, vous devez importer chaque composant que vous utilisez :
import { View, Text, Image, ScrollView } from 'react-native';
En HTML, les balises sont disponibles par défaut. En React Native, chaque composant doit être importé depuis 'react-native'.
View - Le conteneur universel
View est l'équivalent de <div> en HTML. C'est le conteneur de base pour la mise en page.
// React web
<div className="container">
<div className="header">...</div>
<div className="content">...</div>
</div>
// React Native
<View style={styles.container}>
<View style={styles.header}>...</View>
<View style={styles.content}>...</View>
</View>
Propriétés importantes de View
- style : objet de styles (pas de className en React Native)
- Utilise Flexbox par défaut (direction :
column, pasrow) - Ne peut pas afficher de texte directement : il faut utiliser
<Text>à l'intérieur
// ❌ ERREUR : texte directement dans View
<View>
Bonjour !
</View>
// ✅ CORRECT : texte dans un composant Text
<View>
<Text>Bonjour !</Text>
</View>
Pas de texte dans View
Contrairement à <div> en HTML, <View> ne peut pas contenir du texte directement. Tout texte doit être encapsulé dans un composant <Text>. C'est l'erreur la plus courante quand on vient du web.
Que se passe-t-il si vous écrivez <View>Bonjour</View> ?
Text - Afficher du texte
Text remplace toutes les balises de texte HTML (<p>, <h1>, <h2>, <span>, <strong>). Il n'y a qu'un seul composant pour tout le texte.
// React web : plusieurs balises pour le texte
<h1>Titre principal</h1>
<h2>Sous-titre</h2>
<p>Un paragraphe avec du <strong>gras</strong> et de l'<em>italique</em>.</p>
// React Native : un seul composant Text, les styles différencient
<Text style={styles.title}>Titre principal</Text>
<Text style={styles.subtitle}>Sous-titre</Text>
<Text style={styles.paragraph}>
Un paragraphe avec du <Text style={styles.bold}>gras</Text> et de l'
<Text style={styles.italic}>italique</Text>.
</Text>
Text peut être imbriqué
Le composant Text peut contenir d'autres composants Text pour appliquer des styles différents à des portions de texte :
import { View, Text, StyleSheet } from 'react-native';
function ArticleHeader() {
return (
<View style={styles.container}>
<Text style={styles.title}>React Native</Text>
<Text style={styles.meta}>
Par <Text style={styles.author}>Alice Dupont</Text> - 5 min de lecture
</Text>
<Text style={styles.description}>
Apprenez à créer des applications mobiles avec{' '}
<Text style={styles.highlight}>JavaScript</Text> et{' '}
<Text style={styles.highlight}>React</Text>.
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
},
title: {
fontSize: 28,
fontWeight: 'bold',
marginBottom: 8,
},
meta: {
fontSize: 14,
color: '#666',
marginBottom: 12,
},
author: {
fontWeight: 'bold',
color: '#333',
},
description: {
fontSize: 16,
lineHeight: 24,
},
highlight: {
fontWeight: 'bold',
color: '#0066cc',
},
});
Pas de balises sémantiques
En HTML, vous avez <h1>, <h2>, <p>, <strong>, <em> pour donner du sens au contenu. En React Native, il n'y a que <Text>. La distinction visuelle se fait uniquement par les styles (fontSize, fontWeight, etc.). Cela signifie que c'est à vous de structurer visuellement votre texte.
Comment afficher du texte en gras dans React Native ?
Image - Afficher des images
Le composant Image remplace <img> mais fonctionne différemment selon la source de l'image.
Images distantes (URL)
// React web
<img src="https://picsum.photos/200" alt="Photo" />
// React Native - la source est un objet avec uri
<Image
source={{ uri: 'https://picsum.photos/200' }}
style={{ width: 200, height: 200 }}
/>
Dimensions obligatoires pour les images distantes
En React Native, les images chargées depuis une URL n'ont pas de dimensions par défaut. Vous devez obligatoirement spécifier width et height dans le style. Sans ces propriétés, l'image ne s'affiche pas (taille 0x0).
// ❌ L'image ne s'affiche pas (0x0 pixels)
<Image source={{ uri: 'https://picsum.photos/200' }} />
// ✅ Dimensions spécifiées
<Image
source={{ uri: 'https://picsum.photos/200' }}
style={{ width: 200, height: 200 }}
/>
Images locales (fichier)
// React Native - import direct du fichier
<Image source={require('../assets/images/logo.png')} style={{ width: 100, height: 100 }} />
Pour les images locales, on utilise require(). React Native détermine automatiquement les dimensions à partir du fichier, mais il est recommandé de les spécifier pour des performances optimales.
Propriété resizeMode
La propriété resizeMode contrôle comment l'image s'adapte à son conteneur :
<Image
source={{ uri: 'https://picsum.photos/400/200' }}
style={{ width: 200, height: 200 }}
resizeMode="cover" // Remplit le conteneur, recadre si nécessaire (défaut)
/>
<Image
source={{ uri: 'https://picsum.photos/400/200' }}
style={{ width: 200, height: 200 }}
resizeMode="contain" // L'image entière est visible, avec des marges si nécessaire
/>
- cover (défaut) : remplit tout l'espace, recadre si le ratio est différent
- contain : affiche l'image entière, laisse des espaces vides si nécessaire
- stretch : étire l'image pour remplir exactement l'espace
- center : centre l'image sans la redimensionner
Pourquoi une image distante ne s'affiche pas si on ne spécifie pas width et height ?
ScrollView - Zone défilable
Sur le web, le navigateur gère automatiquement le défilement quand le contenu dépasse la taille de la fenêtre. En React Native, il n'y a pas de scroll automatique. Vous devez utiliser ScrollView explicitement.
// React web : le scroll est automatique
<div>
<p>Beaucoup de contenu...</p>
{/* Le navigateur gère le scroll automatiquement */}
</div>
// React Native : il faut utiliser ScrollView
<ScrollView>
<Text>Beaucoup de contenu...</Text>
{/* Sans ScrollView, le contenu déborde sans scroll possible */}
</ScrollView>
Exemple pratique
import { ScrollView, View, Text, StyleSheet } from 'react-native';
function ArticleList() {
const articles = [
{ id: 1, title: 'Introduction à React Native', preview: 'Découvrez les bases...' },
{ id: 2, title: 'Composants primitifs', preview: 'View, Text, Image...' },
{ id: 3, title: 'StyleSheet et Flexbox', preview: 'Mise en page mobile...' },
{ id: 4, title: 'Navigation', preview: 'Se déplacer entre écrans...' },
{ id: 5, title: 'État et formulaires', preview: 'useState sur mobile...' },
{ id: 6, title: 'API et données', preview: 'Fetch et affichage...' },
];
return (
<ScrollView style={styles.container}>
{articles.map(article => (
<View key={article.id} style={styles.card}>
<Text style={styles.cardTitle}>{article.title}</Text>
<Text style={styles.cardPreview}>{article.preview}</Text>
</View>
))}
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
card: {
backgroundColor: '#fff',
padding: 16,
borderRadius: 8,
marginBottom: 12,
borderWidth: 1,
borderColor: '#e0e0e0',
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 4,
},
cardPreview: {
fontSize: 14,
color: '#666',
},
});
ScrollView vs FlatList
ScrollView rend tous ses enfants d'un coup, même ceux qui ne sont pas visibles. Pour les longues listes (50+ éléments), utilisez plutôt FlatList qui ne rend que les éléments visibles à l'écran (virtualisation). Nous verrons FlatList en Séance 3.
// Pour quelques éléments : ScrollView
<ScrollView>
{items.map(item => <ItemCard key={item.id} item={item} />)}
</ScrollView>
// Pour de longues listes : FlatList (Séance 3)
<FlatList
data={items}
renderItem={({ item }) => <ItemCard item={item} />}
keyExtractor={item => item.id.toString()}
/>
SafeAreaView - Éviter l'encoche
Les téléphones modernes ont une encoche (notch) en haut de l'écran et une barre de navigation en bas. SafeAreaView ajuste automatiquement le contenu pour éviter ces zones.
import { SafeAreaView, View, Text, StyleSheet } from 'react-native';
function HomeScreen() {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.content}>
<Text style={styles.title}>Mon Application</Text>
<Text>Le contenu ne sera pas masqué par l'encoche.</Text>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#f5f5f5',
},
content: {
flex: 1,
padding: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 12,
},
});
SafeAreaView vs SafeAreaProvider
Le SafeAreaView de React Native fonctionne uniquement sur iOS. Pour un comportement cohérent sur les deux plateformes, Expo recommande d'utiliser SafeAreaProvider et SafeAreaView du package react-native-safe-area-context (déjà inclus dans les projets Expo) :
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
// Dans le layout racine
<SafeAreaProvider>
<SafeAreaView style={{ flex: 1 }}>
{/* Votre contenu */}
</SafeAreaView>
</SafeAreaProvider>
Pourquoi utiliser SafeAreaView dans une application React Native ?
Exemple complet : Carte de profil
Voici un exemple qui combine tous les composants primitifs :
import { View, Text, Image, ScrollView, SafeAreaView, StyleSheet } from 'react-native';
function ProfileCard({ name, role, avatar, bio }) {
return (
<View style={styles.card}>
<Image
source={{ uri: avatar }}
style={styles.avatar}
resizeMode="cover"
/>
<View style={styles.info}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.role}>{role}</Text>
<Text style={styles.bio}>{bio}</Text>
</View>
</View>
);
}
const team = [
{
id: 1,
name: 'Alice Dupont',
role: 'Développeuse frontend',
avatar: 'https://i.pravatar.cc/150?img=1',
bio: 'Spécialisée en React et React Native depuis 4 ans.',
},
{
id: 2,
name: 'Bob Martin',
role: 'Designer UI/UX',
avatar: 'https://i.pravatar.cc/150?img=3',
bio: 'Conception d\'interfaces utilisateur pour applications mobiles.',
},
{
id: 3,
name: 'Claire Rousseau',
role: 'Développeuse backend',
avatar: 'https://i.pravatar.cc/150?img=5',
bio: 'API REST et bases de données pour applications mobiles.',
},
];
export default function TeamScreen() {
return (
<SafeAreaView style={styles.safeArea}>
<ScrollView style={styles.container}>
<Text style={styles.title}>Notre équipe</Text>
{team.map(member => (
<ProfileCard key={member.id} {...member} />
))}
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#f5f5f5',
},
container: {
flex: 1,
padding: 16,
},
title: {
fontSize: 28,
fontWeight: 'bold',
marginBottom: 16,
},
card: {
flexDirection: 'row',
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
borderWidth: 1,
borderColor: '#e0e0e0',
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
},
info: {
flex: 1,
marginLeft: 16,
},
name: {
fontSize: 18,
fontWeight: 'bold',
},
role: {
fontSize: 14,
color: '#0066cc',
marginBottom: 4,
},
bio: {
fontSize: 14,
color: '#666',
lineHeight: 20,
},
});
Cet exemple montre :
SafeAreaViewpour éviter l'encocheScrollViewpour le défilementViewpour la mise en page (conteneur + carte avecflexDirection: 'row')Textpour le texte (titre, nom, rôle, bio)Imagepour l'avatar (avec dimensions et resizeMode).map()pour afficher une liste (identique à React web)
Comment afficher une liste d'éléments en React Native ?
À retenir
Comprendre, pas mémoriser
Composants primitifs :
View=<div>: conteneur de base, ne peut PAS afficher du texte directementText=<p>,<h1>,<span>: le seul composant pour afficher du texteImage:source={{ uri: '...' }}+ dimensions obligatoires pour les images distantesScrollView: nécessaire pour le défilement (pas automatique comme sur le web)SafeAreaView: évite l'encoche et la barre d'état
Différences avec le web :
- Pas de balises HTML : tout est importé depuis
'react-native' - Pas de
className: les styles passent par la propstyle - Pas de scroll automatique : il faut utiliser
ScrollView - Texte uniquement dans
<Text>: jamais directement dans<View>