0 XP
?
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 6
code
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}

🌳 Arbre de décision — Quelle solution choisir ?

La donnée est-elle utilisée par un seul composant ?