Intermédiaire70 min45 XP
Objectifs de cette leçon
- Gérer l'état local avec useState et useEffect
- Comprendre la gestion d'état global
- Éviter le prop drilling
Gestion d'état en React Native 🗃️
L'état (state) d'une app, c'est toutes les données qui peuvent changer : texte saisi, utilisateur connecté, panier d'achat, etc. React Native offre plusieurs façons de gérer cet état.
1. État local : useState / useReducer 🎯
' useState = pour une variable simple ' useReducer = pour une logique complexe (plusieurs cas) ' useState : ' const [compteur, setCompteur] = useState(0); ' setCompteur(compteur + 1); → met à jour ' useReducer : ' const [state, dispatch] = useReducer(reducer, initialState); ' dispatch({ type: "INCREMENT" }); → action ``' ```tsx import { useReducer } from "react"; // Exemple avec useReducer pour un compteur type Action = { type: "INCREMENT" } | { type: "DECREMENT" } | { type: "RESET" }; function compteurReducer(state: number, action: Action): number { switch (action.type) { case "INCREMENT": return state + 1; case "DECREMENT": return state - 1; case "RESET": return 0; } } function Compteur() { const [compte, dispatch] = useReducer(compteurReducer, 0); return ( <View> <Text>Compte : {compte}</Text> <TouchableOpacity onPress={() => dispatch({ type: "INCREMENT" })}> <Text>+1</Text> </TouchableOpacity> </View> ); } ``' --- ## 2. État global : Context API 📡 ```text ' Context API = partager l'état entre plusieurs composants ' sans passer par les props à chaque niveau ' Utile pour : thème (clair/sombre), langue, utilisateur connecté ' Évite le "prop drilling" (passer les props de parent en parent) ``' tsx import { createContext, useContext, useState } from "react"; // 1. Création du contexte interface ThemeContextType { theme: "clair" | "sombre"; toggleTheme: () => void; } const ThemeContext = createContext<ThemeContextType>({ theme: "clair", toggleTheme: () => {}, }); // 2. Provider (englobe toute l'app) function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState<"clair" | "sombre">("clair"); const toggleTheme = () => { setTheme(theme === "clair" ? "sombre" : "clair"); }; return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); } // 3. Consommation dans n'importe quel composant function BoutonTheme() { const { theme, toggleTheme } = useContext(ThemeContext); return ( <TouchableOpacity onPress={toggleTheme}> <Text>Mode : {theme === "clair" ? "☀️" : "🌙"}</Text> </TouchableOpacity> ); } ``' --- ## 3. Zustand — la solution légère ⚡ ```text ' Zustand = une bibliothèque SUPER simple pour l'état global ' Beaucoup plus simple que Redux, très utilisée aujourd'hui ' Avantages : ' • API minimale ' • Pas de Provider à envelopper ' • Fonctionne avec TypeScript ' • Performant (pas de re-renders inutiles) ``' ```tsx import { create } from "zustand"; // 1. Définition du store (l'état global) interface PanierStore { articles: Article[]; total: number; ajouterArticle: (article: Article) => void; retirerArticle: (id: number) => void; viderPanier: () => void; } const usePanierStore = create<PanierStore>((set) => ({ articles: [], total: 0, ajouterArticle: (article) => set((state) => ({ articles: [...state.articles, article], total: state.total + article.prix, })), retirerArticle: (id) => set((state) => ({ articles: state.articles.filter((a) => a.id !== id), total: state.total - (state.articles.find((a) => a.id === id)?.prix || 0), })), viderPanier: () => set({ articles: [], total: 0 }), })); // 2. Utilisation dans n'importe quel composant function Panier() { const articles = usePanierStore((state) => state.articles); const total = usePanierStore((state) => state.total); const retirerArticle = usePanierStore((state) => state.retirerArticle); return ( <FlatList data={articles} renderItem={({ item }) => ( <View> <Text>{item.nom} - {item.prix}€</Text> <TouchableOpacity onPress={() => retirerArticle(item.id)}> <Text>Retirer</Text> </TouchableOpacity> </View> )} keyExtractor={(item) => item.id} /> ); } ``' --- ## 4. React Query (TanStack Query) pour les API 🔄 ```text ' React Query = gestion D'ÉTAT SERVEUR (données venant d'une API) ' Il gère automatiquement : cache, rechargement, erreurs, loading ' Sans React Query : tu écris TOUT à la main (useState + useEffect) ' Avec React Query : 3 lignes et tout est géré ! ``' ```tsx import { useQuery, useMutation, QueryClient, QueryClientProvider } from "@tanstack/react-query"; // 1. Provider (dans App.tsx) const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> <MonApp /> </QueryClientProvider> ); } // 2. Utilisation function ListeUtilisateurs() { const { data, isLoading, error, refetch } = useQuery({ queryKey: ["utilisateurs"], queryFn: async () => { const res = await fetch("https://api.exemple.com/users"); return res.json(); }, }); if (isLoading) return <Text>Chargement...</Text>; if (error) return <Text>Erreur : {error.message}</Text>; return ( <FlatList data={data} renderItem={({ item }) => <Text>{item.nom}</Text>} onRefresh={refetch} refreshing={isLoading} /> ); } ``' --- ## 5. Redux Toolkit (pour les grosses apps) 🏭 ```text ' Redux Toolkit = la version moderne de Redux ' Utilisé dans les grandes entreprises (moins de boilerplate) ' À utiliser quand : ' • App très complexe avec beaucoup d'état partagé ' • Équipe nombreuse (Redux est très structuré) ' • Middleware nécessaire (logging, persistance) ' Sinon → Zustand ou Context API sont suffisants ! ``' --- ## Schéma récapitulatif 🧭 ```mermaid flowchart TD A["🧠 Gestion d'état"] --> B["Éta local<br/>useState / useReducer"] A --> C["Éta global simple<br/>Context API"] A --> D["Éta global moderne<br/>Zustand ⚡"] A --> E["Données serveur<br/>React Query"] A --> F["Grosse app<br/>Redux Toolkit"] B --> G["Variable locale<br/>à un composant"] C --> H["Thème, langue,<br/>utilisateur"] D --> I["Panier, préférences,<br/>notifications"] E --> J["API, cache,<br/>revalidation"] F --> K["App d'entreprise<br/>Complexe"] ``' --- ## Exercices pour toi 🎯 1. **Context** : crée un contexte pour le thème (clair/sombre) avec un bouton pour basculer 2. **Zustand** : crée un store pour gérer une liste de favoris (ajout, retrait, vidage) 3. **React Query** : utilise useQuery pour afficher des posts depuis JSONPlaceholder avec pull-to-refresh
Création des variables pas à pas
Prêt à explorer la mémoire
Appuie sur Play pour voir les variables se créer une par une
0/5
Visualisation d'exécution
ligne 6code
1import { useState } from 'react';
2import { View, Text, TouchableOpacity } from 'react-native';
3
4export default function Compteur() {
5 const [compte, setCompte] = useState(0);
6
7 return (
8 <View style={{ alignItems: 'center' }}>
9 <Text style={{ fontSize: 48 }}>{compte}</Text>
10 <TouchableOpacity onPress={() => setCompte(compte + 1)}>
11 <Text>+1</Text>
12 </TouchableOpacity>
13 <TouchableOpacity onPress={() => setCompte(compte - 1)}>
14 <Text>-1</Text>
15 </TouchableOpacity>
16 </View>
17 );
18}
Variables
compte=0 (s'incrémente au clic)
setCompte=fonction de mise à jour
📍 État local vs 🌍 État global
Clique sur les boîtes pour voir la différence📍 État Local (useState)
🌍 État Global (Zustand / Redux)
🏠Headertheme, user
🛍️PageProduitcartItems
🛒PagePaniercartItems
👤PageProfiluser, writes: user
🔄 Cycle de vie useState
0
1. État initial
count = 0, en attente
2. Interaction
clic détecté !
3. Appel setState
React planifie la mise à jour
4. Re-render
Le Virtual DOM est recalculé
5. UI mise à jour
Le nouveau texte s'affiche
const [count, setCount] = useState(0);
🗄️ Flux de données — Store Global
Une action propage à tous les connectés🗄️ Store Global
{
"user": null,
"cartItems": [],
"theme": "light"
}🏠
Header
lit: theme, user
🛍️
PageProduit
écrit: cartItems
🛒
PagePanier
0 articles
👤
PageProfil
lit/écrit: user
🔧 Props Drilling vs État Global
📱App
← user={user}
📐Layout⚠️
← user={user}
📋Sidebar⚠️
← user={user}
👤ProfilCard
← user={user}