Ce que vous saurez faire
- Créer projet Expo React Native
- Navigation, API du téléphone, stockage
- Formulaires validés
- Build et publication Play Store / App Store
Étape 1 — Setup Expo
npx create-expo-app@latest mon-app --template
cd mon-app
npx expo start
# Scannez QR avec Expo Go (iOS/Android)
Étape 2 — Premier écran
// app/index.tsx
import { View, Text, FlatList, Pressable, StyleSheet } from "react-native";
import { useState, useEffect } from "react";
type Client = { id: number; nom: string; ville: string };
export default function Accueil() {
const [clients, setClients] = useState<Client[]>([]);
useEffect(() => {
fetch("https://api.example.sn/v1/clients")
.then(r => r.json()).then(setClients);
}, []);
return (
<View style={styles.container}>
<Text style={styles.titre}>Clients</Text>
<FlatList
data={clients}
keyExtractor={c => String(c.id)}
renderItem={({item}) => (
<Pressable style={styles.carte} android_ripple={{color:"#eef"}}>
<Text style={styles.nom}>{item.nom}</Text>
<Text style={styles.ville}>{item.ville}</Text>
</Pressable>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex:1, padding:16, backgroundColor:"#fff" },
titre: { fontSize:24, fontWeight:"600", marginBottom:16 },
carte: { paddingVertical:12, borderBottomWidth:1, borderColor:"#eee" },
nom: { fontSize:16, fontWeight:"500" },
ville: { color:"#666", marginTop:2 },
});
Étape 3 — Navigation Expo Router
app/
_layout.tsx
index.tsx /
clients/
index.tsx /clients
[id].tsx /clients/42
new.tsx /clients/new
(tabs)/
profil.tsx
parametres.tsx
// app/_layout.tsx
import { Stack } from "expo-router";
export default function Layout() {
return (
<Stack screenOptions={{
headerStyle: {backgroundColor:"#1E88E5"},
headerTintColor:"#fff"
}}>
<Stack.Screen name="index" options={{title:"Accueil"}} />
<Stack.Screen name="clients/[id]" options={{title:"Fiche client"}} />
</Stack>
);
}
Étape 4 — API du téléphone
npx expo install expo-location expo-image-picker expo-notifications
import * as Location from "expo-location";
import * as ImagePicker from "expo-image-picker";
async function prendrePosition() {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") throw new Error("Permission refusée");
const pos = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High
});
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
}
async function choisirPhoto() {
const r = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 0.7,
base64: true,
});
if (!r.canceled) return r.assets[0].uri;
}
Étape 5 — Stockage local
npx expo install @react-native-async-storage/async-storage expo-secure-store
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from "expo-secure-store";
// Préférences
await AsyncStorage.setItem("theme", "dark");
const theme = await AsyncStorage.getItem("theme");
// Sensibles (chiffré keychain/keystore)
await SecureStore.setItemAsync("auth_token", "ey...");
const token = await SecureStore.getItemAsync("auth_token");
Étape 6 — Formulaires React Hook Form + Zod
npm install react-hook-form zod @hookform/resolvers
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { TextInput, Text, Button, View } from "react-native";
const Schema = z.object({
nom: z.string().min(2, "Nom trop court"),
telephone: z.string().regex(/^\+221 ?[0-9]{9}$/, "Format +221 XXXXXXXXX"),
});
export function FormClient() {
const { control, handleSubmit, formState: {errors} } = useForm({
resolver: zodResolver(Schema)
});
const sauver = (data: z.infer<typeof Schema>) =>
fetch("/api/clients", {method:"POST", body:JSON.stringify(data)});
return (
<View style={{padding:16, gap:12}}>
<Controller control={control} name="nom"
render={({field:{onChange, value}}) => (
<TextInput onChangeText={onChange} value={value}
placeholder="Nom" style={{borderWidth:1, padding:8}} />
)} />
{errors.nom && <Text style={{color:"red"}}>{errors.nom.message}</Text>}
<Button title="Enregistrer" onPress={handleSubmit(sauver)} />
</View>
);
}
Étape 7 — Notifications push
import * as Notifications from "expo-notifications";
// Demander permission
const { status } = await Notifications.requestPermissionsAsync();
// Obtenir Expo push token
const token = (await Notifications.getExpoPushTokenAsync()).data;
// Envoyer à votre backend
// Envoyer depuis backend
await fetch("https://exp.host/--/api/v2/push/send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
to: token,
title: "Nouvelle facture",
body: "Facture 042 à régler"
})
});
Étape 8 — Build EAS
npx eas-cli build:configure
# Android
npx eas build --platform android
# iOS (requiert Mac ou service cloud Apple)
npx eas build --platform ios
Étape 9 — Submit stores
# Play Store
npx eas submit --platform android --latest
# App Store
npx eas submit --platform ios --latest
Étape 10 — OTA updates
npx eas update --branch production --message "Bug fix v1.0.1"
# Les utilisateurs reçoivent le JS mis à jour SANS passer par les stores
Checklist
✓ Expo SDK 51+ pour stabilité
✓ Expo Router file-based
✓ SecureStore pour tokens
✓ React Hook Form + Zod pour formulaires
✓ Notifications push configurées
✓ EAS Build pour prod
✓ OTA updates pour bugs mineurs
✓ Test Android + iOS avant release
Besoin d'un site web ?
Confiez-nous la Création de Votre Site Web
Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.
À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité