Développement Mobile Cross-Plateforme avec React Native : Créez des Applications iOS et Android Performantes
Développement Mobile Cross-Plateforme avec React Native : Créez des Applications iOS et Android Performantes

Intégration d'APIs et Requêtes Réseau dans React Native

Bienvenue dans cette leçon fondamentale sur l'intégration d'APIs (Application Programming Interfaces) et la gestion des requêtes réseau dans vos applications React Native. Dans le monde du développement mobile, rares sont les applications qui fonctionnent de manière isolée. La plupart interagissent avec des services externes pour récupérer, envoyer ou synchroniser des données. Comprendre comment interagir avec ces services est donc une compétence indispensable.

Introduction : Le Cœur des Applications Connectées

Les applications mobiles modernes ne sont plus de simples outils autonomes. Elles sont des portails vers une multitude de services, de contenus dynamiques et d'interactions utilisateur riches. C'est là que les APIs entrent en jeu.

Qu'est-ce qu'une API ?

Une API est un ensemble de règles et de définitions qui permet à différentes applications logicielles de communiquer entre elles. Imaginez une API comme le menu d'un restaurant : il liste ce que vous pouvez commander (les requêtes) et ce que vous pouvez attendre en retour (les réponses), sans avoir à savoir comment le plat est préparé en cuisine.

Pour une application mobile, une API est généralement une interface web qui permet :

  • De récupérer des données (ex: liste de produits, articles de blog, données météo).
  • D'envoyer des données (ex: créer un nouveau compte utilisateur, poster un commentaire, soumettre un formulaire).
  • De modifier des données existantes (ex: mettre à jour un profil utilisateur).
  • De supprimer des données.

Pourquoi l'intégration d'APIs est-elle cruciale pour les applications mobiles ?

  • Contenu Dynamique : Afficher des informations qui évoluent constamment (actualités, cours de bourse, posts de réseaux sociaux).
  • Fonctionnalités Étendues : Intégrer des services tiers (paiement en ligne, cartes géographiques, authentification via Google/Facebook).
  • Persistance des Données : Sauvegarder les données utilisateurs ou d'application sur un serveur pour qu'elles soient accessibles depuis n'importe quel appareil.
  • Collaboration et Synchronisation : Permettre à plusieurs utilisateurs d'interagir avec les mêmes données ou de synchroniser des informations entre différentes plateformes.

Dans cette leçon, nous allons explorer les concepts clés des requêtes réseau, les outils disponibles dans React Native pour les gérer, et les bonnes pratiques pour construire des applications robustes et performantes.

Comprendre les Fondamentaux des APIs et Requêtes Réseau

Avant de plonger dans le code React Native, il est essentiel de maîtriser les concepts sous-jacents aux interactions avec les APIs web.

Qu'est-ce qu'une API RESTful ?

La majorité des APIs web modernes que vous rencontrerez sont basées sur le style architectural REST (Representational State Transfer). Une API RESTful adhère à plusieurs principes, dont les plus importants sont :

  • Ressources : Tout est une ressource (un utilisateur, un produit, une commande). Chaque ressource est identifiée par une URL unique (Uniform Resource Locator).
    • Ex: https://api.monservice.com/utilisateurs
    • Ex: https://api.monservice.com/produits/123
  • Verbes HTTP : Les opérations sur ces ressources sont définies par les verbes HTTP.
  • Communication sans État (Stateless) : Chaque requête du client au serveur contient toutes les informations nécessaires pour que le serveur puisse comprendre et traiter la requête. Le serveur ne conserve aucune information sur les requêtes précédentes du client.

Verbes HTTP Courants

Les verbes HTTP, également appelés méthodes HTTP, indiquent le type d'action que le client souhaite effectuer sur la ressource identifiée par l'URL.

  • GET : Récupérer des données. C'est le verbe le plus courant. Il est utilisé pour demander des données à une ressource spécifique ou à une collection de ressources.
    • Ex: GET /produits (récupérer tous les produits)
    • Ex: GET /produits/123 (récupérer le produit avec l'ID 123)
  • POST : Créer une nouvelle ressource. Les données à créer sont envoyées dans le corps de la requête.
    • Ex: POST /produits (créer un nouveau produit)
  • PUT : Mettre à jour une ressource existante, ou la créer si elle n'existe pas. Il remplace généralement la ressource complète.
    • Ex: PUT /produits/123 (remplacer les données du produit 123)
  • DELETE : Supprimer une ressource.
    • Ex: DELETE /produits/123 (supprimer le produit 123)
  • PATCH : Mettre à jour partiellement une ressource existante. (Moins fréquent que PUT mais utile pour de petites modifications).

Format d'Échange de Données : JSON (JavaScript Object Notation)

Lors de l'échange de données avec une API, un format standard est nécessaire pour que le client et le serveur puissent se comprendre. Le format le plus répandu aujourd'hui est JSON.

  • Léger et Lisible : JSON est un format de texte léger et facilement lisible par les humains et les machines.
  • Basé sur JavaScript : Sa syntaxe est directement dérivée de la notation d'objets JavaScript, ce qui le rend très naturel à manipuler dans un environnement JavaScript comme React Native.
  • Représentation des Données : Il permet de représenter des objets, des tableaux, des chaînes de caractères, des nombres, des booléens et des valeurs nulles.

Exemple de données JSON :

{
  "id": 1,
  "titre": "Apprendre React Native",
  "auteur": {
    "nom": "Jane Doe",
    "email": "jane.doe@example.com"
  },
  "tags": ["mobile", "javascript", "react-native"],
  "estPublie": true
}

Lorsque vous envoyez des données (POST, PUT, PATCH), vous les "sérialisez" en JSON (convertissez un objet JavaScript en chaîne JSON). Lorsque vous recevez des données, vous les "désérialisez" (convertissez une chaîne JSON en objet JavaScript).

Requêtes Asynchrones : Promesses et async/await

Les requêtes réseau prennent du temps. Elles dépendent de la vitesse de votre connexion Internet, de la latence du serveur et de la complexité de l'opération. Si ces requêtes étaient "synchrones" (bloquantes), votre application se figerait complètement pendant l'attente de la réponse du serveur, ce qui est une très mauvaise expérience utilisateur.

C'est pourquoi les requêtes réseau sont asynchrones et non bloquantes. Cela signifie que votre application continue de fonctionner pendant que la requête est en cours en arrière-plan. Une fois la réponse reçue, un "callback" ou une "promesse" est exécutée pour traiter les données.

En JavaScript moderne, la gestion de l'asynchronisme repose principalement sur les Promesses et la syntaxe async/await.

  • Promesse : Un objet qui représente l'achèvement (ou l'échec) éventuel d'une opération asynchrone et sa valeur résultante.

    • Une promesse peut être dans l'un des trois états :
      • pending (en attente) : état initial, ni réalisée ni rejetée.
      • fulfilled (réalisée) : l'opération a réussi, la promesse a une valeur.
      • rejected (rejetée) : l'opération a échoué, la promesse a une raison (erreur).
    • Vous utilisez .then() pour gérer le succès et .catch() pour gérer l'échec.
  • async/await : Une syntaxe plus propre et plus facile à lire pour travailler avec les promesses.

    • Le mot-clé async devant une fonction indique que cette fonction retourne implicitement une promesse.
    • Le mot-clé await peut être utilisé uniquement à l'intérieur d'une fonction async. Il "met en pause" l'exécution de la fonction async jusqu'à ce que la promesse awaitée soit résolue (réussie ou échouée), puis reprend l'exécution avec la valeur résolue.

C'est la méthode privilégiée pour gérer les requêtes réseau dans React Native.

Intégration d'APIs dans React Native

React Native offre plusieurs façons d'effectuer des requêtes réseau. Les deux plus courantes sont l'API fetch native et la bibliothèque tierce Axios.

L'API fetch native

L'API fetch est une API web standard, disponible nativement dans React Native (et dans la plupart des navigateurs modernes). Elle fournit une interface générique pour effectuer des requêtes réseau. C'est l'option par défaut si vous n'avez pas besoin de fonctionnalités avancées.

Points clés de fetch :

  • Basé sur les Promesses : Toutes les opérations fetch retournent des promesses.
  • Simple : Très simple pour les requêtes GET basiques.
  • Gestion manuelle de JSON et des erreurs : Vous devez explicitement appeler .json() pour parser la réponse et vérifier response.ok pour détecter les erreurs HTTP.

Exemple de Requête GET avec fetch

Récupérons une liste d'articles depuis une API de test publique (jsonplaceholder.typicode.com).

import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, ActivityIndicator, StyleSheet, Alert } from 'react-native';

const ArticlesScreen = () => {
  const [articles, setArticles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchArticles = async () => {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts');
        
        // Vérifier si la réponse est un succès (statut 2xx)
        if (!response.ok) {
          // Si non, lancer une erreur avec le statut
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        // Parser la réponse JSON
        const data = await response.json();
        setArticles(data);
      } catch (e) {
        console.error("Erreur lors de la récupération des articles:", e);
        setError(e.message); // Stocker le message d'erreur
        Alert.alert("Erreur", "Impossible de charger les articles. Veuillez réessayer.");
      } finally {
        setLoading(false); // Arrêter l'indicateur de chargement
      }
    };

    fetchArticles();
  }, []); // Le tableau vide [] signifie que ce useEffect ne s'exécute qu'une fois, au montage du composant

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#0000ff" />
        <Text>Chargement des articles...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>Erreur: {error}</Text>
        <Text>Vérifiez votre connexion internet ou réessayez plus tard.</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Liste des Articles</Text>
      <FlatList
        data={articles}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <View style={styles.articleItem}>
            <Text style={styles.articleTitle}>{item.title}</Text>
            <Text style={styles.articleBody}>{item.body.substring(0, 100)}...</Text>
          </View>
        )}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 50,
    paddingHorizontal: 20,
    backgroundColor: '#f5f5f5',
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
    color: '#333',
  },
  articleItem: {
    backgroundColor: '#fff',
    padding: 15,
    borderRadius: 8,
    marginBottom: 10,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.2,
    shadowRadius: 1.41,
    elevation: 2,
  },
  articleTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#007bff',
    marginBottom: 5,
  },
  articleBody: {
    fontSize: 14,
    color: '#555',
    lineHeight: 20,
  },
  errorText: {
    color: 'red',
    fontSize: 16,
    textAlign: 'center',
    marginBottom: 10,
  },
});

export default ArticlesScreen;

Explication du code :

  1. useState Hooks : Nous utilisons useState pour gérer trois états : articles (pour stocker les données reçues), loading (pour indiquer si la requête est en cours), et error (pour stocker un message d'erreur si la requête échoue).
  2. useEffect Hook : Ce hook est utilisé pour déclencher la requête API une fois que le composant est monté. Le tableau de dépendances vide [] garantit qu'il ne s'exécutera qu'une seule fois.
  3. Fonction fetchArticles :
    • Elle est déclarée comme async pour pouvoir utiliser await.
    • setLoading(true) : Met l'état de chargement à vrai avant la requête.
    • await fetch(...) : Exécute la requête GET. Par défaut, fetch fait une requête GET si le paramètre method n'est pas spécifié.
    • if (!response.ok) : Très important ! fetch ne rejette une promesse qu'en cas d'erreur réseau (ex: pas de connexion). Pour les erreurs HTTP (comme 404 Not Found, 500 Internal Server Error), response.ok sera false. Nous devons donc vérifier cela manuellement et throw new Error() pour que le bloc catch le gère.
    • await response.json() : Parse la chaîne de caractères JSON reçue en un objet JavaScript utilisable. Ceci est également une opération asynchrone.
    • setArticles(data) : Met à jour l'état articles avec les données reçues.
    • try...catch...finally : Structure standard pour gérer l'asynchronisme et les erreurs. catch attrape les erreurs réseau ou celles que nous avons levées manuellement. finally s'exécute toujours, que la requête réussisse ou échoue, idéal pour mettre setLoading(false).
  4. Affichage Conditionnel : Nous affichons un ActivityIndicator pendant le chargement, un message d'erreur si error est défini, ou la liste des articles (FlatList) une fois les données chargées.

Exemple de Requête POST avec fetch

Pour envoyer des données, vous devez configurer la méthode (POST), les en-têtes (notamment Content-Type: application/json) et le corps de la requête (les données JSON stringifiées).

import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, ActivityIndicator, Alert } from 'react-native';

const CreateArticleScreen = () => {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const [loading, setLoading] = useState(false);

  const handleCreateArticle = async () => {
    if (!title || !body) {
      Alert.alert("Erreur", "Veuillez remplir tous les champs.");
      return;
    }

    setLoading(true);
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
        method: 'POST', // Spécifier la méthode POST
        headers: {
          'Content-Type': 'application/json', // Indiquer que le corps est du JSON
        },
        body: JSON.stringify({ // Convertir l'objet JS en chaîne JSON
          title: title,
          body: body,
          userId: 1, // Un userId est souvent requis par l'API
        }),
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const newArticle = await response.json();
      console.log('Article créé avec succès:', newArticle);
      Alert.alert("Succès", `Article "${newArticle.title}" créé avec l'ID: ${newArticle.id}`);
      setTitle(''); // Réinitialiser les champs
      setBody('');
    } catch (e) {
      console.error("Erreur lors de la création de l'article:", e);
      Alert.alert("Erreur", `Impossible de créer l'article: ${e.message}`);
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Créer un Nouvel Article</Text>
      <TextInput
        style={styles.input}
        placeholder="Titre de l'article"
        value={title}
        onChangeText={setTitle}
      />
      <TextInput
        style={styles.textArea}
        placeholder="Contenu de l'article"
        value={body}
        onChangeText={setBody}
        multiline
        numberOfLines={4}
      />
      <Button
        title={loading ? "Création en cours..." : "Créer l'article"}
        onPress={handleCreateArticle}
        disabled={loading}
      />
      {loading && <ActivityIndicator size="small" color="#0000ff" style={styles.indicator} />}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 30,
    textAlign: 'center',
    color: '#333',
  },
  input: {
    height: 50,
    borderColor: '#ddd',
    borderWidth: 1,
    borderRadius: 8,
    paddingHorizontal: 15,
    marginBottom: 15,
    backgroundColor: '#fff',
    fontSize: 16,
  },
  textArea: {
    height: 120,
    borderColor: '#ddd',
    borderWidth: 1,
    borderRadius: 8,
    paddingHorizontal: 15,
    paddingTop: 15, // Pour que le texte ne commence pas tout en haut
    marginBottom: 20,
    backgroundColor: '#fff',
    fontSize: 16,
    textAlignVertical: 'top', // Pour Android, aligne le texte en haut
  },
  indicator: {
    marginTop: 15,
  },
});

export default CreateArticleScreen;

Explication du code :

  1. Le second argument de fetch est un objet de configuration.
  2. method: 'POST' : Définit la méthode HTTP.
  3. headers: { 'Content-Type': 'application/json' } : Indique au serveur que le corps de la requête est au format JSON. C'est crucial pour que le serveur puisse parser correctement les données.
  4. body: JSON.stringify(...) : Convertit l'objet JavaScript contenant les données du nouvel article en une chaîne JSON avant de l'envoyer.

Axios : Une Alternative Populaire

Bien que fetch soit parfaitement fonctionnel, beaucoup de développeurs préfèrent utiliser des bibliothèques tierces comme Axios en raison de ses fonctionnalités supplémentaires et de son ergonomie améliorée.

Pourquoi utiliser Axios ?

  • Gestion automatique de JSON : Axios parse automatiquement les réponses JSON et stringifie les corps de requêtes JSON. Pas besoin de response.json() ni de JSON.stringify().
  • Meilleure gestion des erreurs : Axios rejette automatiquement les promesses pour les statuts HTTP 4xx/5xx (contrairement à fetch), simplifiant la logique try/catch.
  • Intercepteurs : Vous pouvez intercepter les requêtes ou les réponses avant qu'elles ne soient envoyées ou traitées. Très utile pour ajouter des en-têtes d'authentification, gérer les erreurs globales, etc.
  • Annulation de requêtes : Permet d'annuler des requêtes en cours (utile si un composant est démonté avant la fin de la requête).
  • Barre de progression de téléchargement/upload.
  • Support des requêtes côté serveur (Node.js) et côté client.

Installation d'Axios

Axios n'est pas natif, vous devez l'installer :

npm install axios
# ou
yarn add axios

Exemple de Requête GET avec Axios

import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, ActivityIndicator, StyleSheet, Alert } from 'react-native';
import axios from 'axios'; // Importer Axios

const ArticlesAxiosScreen = () => {
  const [articles, setArticles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchArticles = async () => {
      try {
        // Axios parse automatiquement le JSON et rejette pour les erreurs HTTP
        const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
        setArticles(response.data); // Les données sont directement dans response.data
      } catch (e) {
        console.error("Erreur lors de la récupération des articles (Axios):", e);
        // Axios fournit plus de détails sur l'erreur
        if (e.response) {
          // La requête a été faite et le serveur a répondu avec un statut en dehors de la plage 2xx
          setError(`Erreur Serveur: ${e.response.status} - ${e.response.data.message || 'Unknown error'}`);
        } else if (e.request) {
          // La requête a été faite mais aucune réponse n'a été reçue (ex: réseau indisponible)
          setError("Pas de réponse du serveur. Vérifiez votre connexion.");
        } else {
          // Quelque chose d'autre s'est mal passé lors de la configuration de la requête
          setError(`Erreur: ${e.message}`);
        }
        Alert.alert("Erreur", "Impossible de charger les articles. Veuillez réessayer.");
      } finally {
        setLoading(false);
      }
    };

    fetchArticles();
  }, []);

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#0000ff" />
        <Text>Chargement des articles (Axios)...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>Erreur: {error}</Text>
        <Text>Vérifiez votre connexion internet ou réessayez plus tard.</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Liste des Articles (Axios)</Text>
      <FlatList
        data={articles}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <View style={styles.articleItem}>
            <Text style={styles.articleTitle}>{item.title}</Text>
            <Text style={styles.articleBody}>{item.body.substring(0, 100)}...</Text>
          </View>
        )}
      />
    </View>
  );
};

const styles = StyleSheet.create({
    container: {
      flex: 1,
      paddingTop: 50,
      paddingHorizontal: 20,
      backgroundColor: '#f5f5f5',
    },
    center: {
      flex: 1,
      justifyContent: 'center',
      alignItems: 'center',
    },
    title: {
      fontSize: 28,
      fontWeight: 'bold',
      marginBottom: 20,
      textAlign: 'center',
      color: '#333',
    },
    articleItem: {
      backgroundColor: '#fff',
      padding: 15,
      borderRadius: 8,
      marginBottom: 10,
      shadowColor: '#000',
      shadowOffset: { width: 0, height: 1 },
      shadowOpacity: 0.2,
      shadowRadius: 1.41,
      elevation: 2,
    },
    articleTitle: {
      fontSize: 18,
      fontWeight: '600',
      color: '#007bff',
      marginBottom: 5,
    },
    articleBody: {
      fontSize: 14,
      color: '#555',
      lineHeight: 20,
    },
    errorText: {
      color: 'red',
      fontSize: 16,
      textAlign: 'center',
      marginBottom: 10,
    },
  });

export default ArticlesAxiosScreen;

Explication du code :

  1. import axios from 'axios'; : Importe la bibliothèque.
  2. await axios.get(...) : Exécute une requête GET. Axios fournit des méthodes raccourcies pour chaque verbe HTTP (axios.post, axios.put, etc.).
  3. response.data : Axios enveloppe la réponse HTTP et place les données JSON directement dans la propriété data. Pas besoin de response.json().
  4. Gestion des erreurs : C'est là qu'Axios brille. Si le statut HTTP n'est pas dans la plage 2xx, la promesse est automatiquement rejetée. L'objet e dans le bloc catch est un objet AxiosError qui contient des propriétés utiles comme e.response (pour les erreurs du serveur), e.request (pour les erreurs réseau) ou e.message (pour d'autres erreurs).

Exemple de Requête POST avec Axios

import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, ActivityIndicator, Alert } from 'react-native';
import axios from 'axios';

const CreateArticleAxiosScreen = () => {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const [loading, setLoading] = useState(false);

  const handleCreateArticle = async () => {
    if (!title || !body) {
      Alert.alert("Erreur", "Veuillez remplir tous les champs.");
      return;
    }

    setLoading(true);
    try {
      // Axios prend directement l'objet JS comme corps de requête
      // et gère automatiquement l'en-tête Content-Type et JSON.stringify
      const response = await axios.post('https://jsonplaceholder.typicode.com/posts', {
        title: title,
        body: body,
        userId: 1,
      });

      const newArticle = response.data; // Les données de la réponse sont dans .data
      console.log('Article créé avec succès (Axios):', newArticle);
      Alert.alert("Succès", `Article "${newArticle.title}" créé avec l'ID: ${newArticle.id}`);
      setTitle('');
      setBody('');
    } catch (e) {
      console.error("Erreur lors de la création de l'article (Axios):", e);
      if (e.response) {
        Alert.alert("Erreur", `Impossible de créer l'article: ${e.response.status} - ${e.response.data.message || 'Erreur serveur'}`);
      } else {
        Alert.alert("Erreur", `Impossible de créer l'article: ${e.message}`);
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Créer un Nouvel Article (Axios)</Text>
      <TextInput
        style={styles.input}
        placeholder="Titre de l'article"
        value={title}
        onChangeText={setTitle}
      />
      <TextInput
        style={styles.textArea}
        placeholder="Contenu de l'article"
        value={body}
        onChangeText={setBody}
        multiline
        numberOfLines={4}
      />
      <Button
        title={loading ? "Création en cours..." : "Créer l'article"}
        onPress={handleCreateArticle}
        disabled={loading}
      />
      {loading && <ActivityIndicator size="small" color="#0000ff" style={styles.indicator} />}
    </View>
  );
};

const styles = StyleSheet.create({
    container: {
      flex: 1,
      padding: 20,
      justifyContent: 'center',
      backgroundColor: '#f5f5f5',
    },
    title: {
      fontSize: 24,
      fontWeight: 'bold',
      marginBottom: 30,
      textAlign: 'center',
      color: '#333',
    },
    input: {
      height: 50,
      borderColor: '#ddd',
      borderWidth: 1,
      borderRadius: 8,
      paddingHorizontal: 15,
      marginBottom: 15,
      backgroundColor: '#fff',
      fontSize: 16,
    },
    textArea: {
      height: 120,
      borderColor: '#ddd',
      borderWidth: 1,
      borderRadius: 8,
      paddingHorizontal: 15,
      paddingTop: 15,
      marginBottom: 20,
      backgroundColor: '#fff',
      fontSize: 16,
      textAlignVertical: 'top',
    },
    indicator: {
      marginTop: 15,
    },
  });

export default CreateArticleAxiosScreen;

Explication du code :

  1. await axios.post('URL', { ...data }) : Le deuxième argument de axios.post est l'objet JavaScript contenant les données à envoyer. Axios s'occupe de le stringifier en JSON et de définir l'en-tête Content-Type approprié. C'est beaucoup plus concis qu'avec fetch.

En résumé sur fetch vs Axios :

  • fetch : Natif, plus léger, parfait pour les requêtes simples si vous êtes à l'aise avec la gestion manuelle du JSON et des erreurs.
  • Axios : Nécessite une installation, offre une meilleure ergonomie, une gestion d'erreur plus intuitive et des fonctionnalités avancées comme les intercepteurs. Pour la plupart des applications complexes, Axios est souvent préféré.

Gestion des États et Affichage des Données

Comme illustré dans les exemples, l'intégration d'APIs va de pair avec la gestion d'état de votre composant.

  • useState : Permet de stocker les données récupérées (articles), l'état de chargement (loading), et les messages d'erreur (error). Ces états sont utilisés pour rendre l'interface utilisateur dynamique.
  • useEffect : Est le hook idéal pour déclencher des effets secondaires comme les requêtes réseau. Lorsqu'il est utilisé avec un tableau de dépendances vide ([]), la requête est effectuée une seule fois au montage du composant. Si vous voulez refetcher des données lorsque certaines propriétés ou états changent, vous pouvez les inclure dans ce tableau.
  • Affichage Conditionnel : C'est la technique par excellence pour gérer les différents états d'une requête :
    • Afficher un indicateur de chargement (ActivityIndicator) tant que loading est true.
    • Afficher un message d'erreur si error n'est pas null.
    • Afficher les données réelles une fois qu'elles sont chargées et qu'il n'y a pas d'erreur.

Bonnes Pratiques et Sécurité

Intégrer des APIs est essentiel, mais le faire de manière sécurisée et performante l'est tout autant.

  • Gestion des Erreurs Robuste

    • Distinguerez les erreurs réseau (pas de connexion, timeout) des erreurs API (404, 500, 401 Unauthorized).
    • Informez l'utilisateur clairement en cas d'erreur (messages pertinents, toasts, alerts).
    • Utilisez des blocs try...catch...finally pour encapsuler vos requêtes.
  • Indicateurs de Chargement (UX)

    • Toujours montrer un ActivityIndicator ou un squelette de contenu pendant qu'une requête est en cours. Cela améliore considérablement l'expérience utilisateur en évitant l'impression que l'application est figée.
  • Gestion des Données Sensibles (Clés API, Tokens)

    • NE JAMAIS coder en dur des clés API, des secrets ou des identifiants sensibles directement dans votre code source ou dans votre dépôt Git public.
    • Utilisez des variables d'environnement pour stocker ces informations (par exemple, via la bibliothèque react-native-dotenv ou des configurations de build natives).
    • Pour les tokens d'authentification (JWT par exemple), stockez-les de manière sécurisée sur l'appareil (ex: react-native-keychain pour iOS Keychain et Android Keystore) et incluez-les dans les en-têtes de vos requêtes (généralement Authorization: Bearer VOTRE_TOKEN).
  • Découplage des Requêtes API (Services Dédicés)

    • Ne mélangez pas la logique de requête API avec la logique de votre composant UI. Créez un dossier services ou api avec des fichiers dédiés à chaque ressource ou groupe d'endpoints.
    • Exemple de structure :
      - src/
        - components/
        - screens/
        - api/
          - axiosInstance.js // Configuration de base d'Axios (URL de base, intercepteurs)
          - auth.js          // Fonctions pour l'authentification (login, register)
          - articles.js      // Fonctions pour les articles (getArticles, createArticle)
          - users.js         // Fonctions pour les utilisateurs (getUserProfile, updateUser)
        - App.js
      
    • Cela rend votre code plus maintenable, testable et réutilisable.
  • Limitation des Requêtes (Throttling/Debouncing)

    • Si vous avez des fonctionnalités qui peuvent déclencher de nombreuses requêtes rapidement (ex: recherche en temps réel), utilisez des techniques de throttling (limiter la fréquence d'exécution) ou de debouncing (exécuter la fonction seulement après un certain délai d'inactivité) pour éviter de surcharger votre API et le réseau. Des bibliothèques comme lodash offrent ces utilitaires.
  • Caching

    • Pour les données qui ne changent pas fréquemment, envisagez de mettre en cache les réponses côté client pour améliorer la performance et réduire les requêtes réseau. Des bibliothèques comme React Query (TanStack Query) ou SWR peuvent grandement simplifier la gestion du cache et de la synchronisation des données côté client.

Conclusion

L'intégration d'APIs est la pierre angulaire des applications mobiles connectées. Vous avez maintenant une compréhension solide des concepts fondamentaux derrière les requêtes réseau (HTTP, JSON, asynchrone) et des outils clés disponibles dans React Native (fetch et Axios).

  • Vous savez comment récupérer et envoyer des données en utilisant les méthodes GET et POST.
  • Vous êtes capable de gérer les états de chargement et d'erreur pour offrir une meilleure expérience utilisateur.
  • Vous avez découvert l'importance de la gestion des erreurs et des bonnes pratiques de sécurité et de modularité.

La prochaine étape est la pratique ! Expérimentez avec différentes APIs, créez des interfaces utilisateur qui affichent des données dynamiques, et n'hésitez pas à approfondir les fonctionnalités avancées d'Axios ou d'autres bibliothèques de gestion de données. Le monde des applications connectées est à portée de main !