Articolo originale: https://www.freecodecamp.org/news/how-to-consume-rest-apis-in-react/

React è una popolare libreria frontend che gli sviluppatori usano per creare applicazioni. Se vuoi creare app pronte per la produzione, a un certo punto dovrai integrare le API nella tua applicazione React.

Ogni sviluppatore che voglia costruire applicazioni web moderne e robuste con React deve sapere come utilizzare le API per ottenere dati da inserire nelle proprie applicazioni React.

In questa guida per principianti imparerai come utilizzare API RESTful in React, incluso il recupero, l'eliminazione e l'aggiunta di dati. Esamineremo anche i due modi principali per utilizzare le API RESTful e come usarle con gli hook di React.

Cos'è una API REST?

Se hai mai dedicato del tempo a programmare o fare ricerche sulla programmazione, probabilmente ti sei imbattuto nel termine "API".

API sta per Application Programming Interface (Interfaccia di Programmazione per un'Applicazione). È un mezzo che consente a diverse applicazioni di comunicare tra loro in modo programmatico e restituire una risposta in tempo reale.

Roy Fielding ha definito REST nel 2000 come uno stile architetturale e una metodologia comunemente utilizzati nello sviluppo di servizi internet, come i sistemi ipermediali distribuiti. È un acronimo che sta per "REpresentational State Transfer".

Quando viene effettuata una richiesta tramite API REST, viene inviata una rappresentazione dello stato corrente di una risorsa al richiedente o  endpoint. Questa rappresentazione dello stato può essere in formato JSON (JavaScript Object Notation), XML, o HTML.

JSON è il formato di file maggiormente usato in quanto è indipendente dal linguaggio e può essere letto sia dall'uomo che dalle macchine.

Per esempio:

[
   {
      "userId": 1,
      "id": 1,
      "title": "sunt excepturi",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur "
   },
   {
      "userId": 1,
      "id": 2,
      "title": "qui est esse",
      "body": "est rerum tempore vitae\nsequi sint nihil"
   }
]

Come Utilizzare le API REST in React

Ci sono svariati modi di utilizzare una API REST in una applicazione React, ma in questa guida, daremo uno sguardo a due degli approcci più popolari:  Axios (un client HTTP basato su promise) e l'API Fetch (una API web integrata nel browser).

Nota: Dovresti avere familiarità con JavaScript, React, e gli hook React, poiché sono fondamentali per una piena comprensione di questa guida.

Prima di approfondire come utilizzare le API, è importante capire che l'utilizzo di API in React è molto diverso da come avviene in JavaScript. Questo perché le richieste vengono eseguite in un Componente React.

Nel nostro caso utilizzeremo componenti funzionali, il che significa che dovremo usare due dei principali hook di React:

  • hook useEffect: in React, eseguiamo le richieste API all'interno dell'hook useEffect() . Con questo hook il codice viene eseguito immediatamente quando il componente che lo contiene viene caricato oppure dopo che viene raggiunto uno specifico stato. Questa è la sintassi generale che useremo:
useEffect(() => {
    // qui il recupero dei dati
}, []);
  • hook useState: quando richiediamo dati, dobbiamo preparare uno stato nel quale i dati verranno conservati una volta restituiti. Possiamo salvare lo stato in uno strumento per la gestione dello stato come Redux oppure in un oggetto nel contesto. Per mantenere le cose semplici, conserveremo i dati restituiti in uno stato locale React.
const [posts, setPosts] = useState([]);

Entriamo ora nel vivo di questa guida, dove impareremo come ottenere, aggiungere ed eliminare i dati utilizzando l'API dei post JSONPlaceholder. Questa conoscenza è applicabile a qualsiasi tipo di API, poiché questa guida è destinata ai principianti.

Come Utilizzare le API Usando l'API Fetch

L'API Fetch è un metodo JavaScript integrato per recuperare risorse da un server o da un endpoint API. Essendo integrato, non necessita di installazione di alcuna dipendenza o pacchetto.

Il metodo fetch() richiede un argomento obbligatorio, che è il percorso o URL della risorsa che si vuole ottenere. Restituisce poi una Promise in modo che tu possa gestire successo o fallimento usando i metodi then() e catch().

Una richiesta di recupero di base è molto semplice da scrivere e ha l'aspetto del codice seguente. Stiamo semplicemente recuperando i dati da un URL che restituisce i dati come JSON, che poi stampiamo nella console:

fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
   .then(response => response.json())
   .then(data => console.log(data));

In genere la risposta predefinita è una normale risposta HTTP invece che l'effettivo JSON, ma possiamo ottenere il nostro risultato come oggetto JSON usando il metodo json() della risposta.

Come Eseguire una Richiesta GET in React con l'API Fetch

Puoi usare il metodo HTTP GET per richiedere dati da un endpoint.

Come detto in precedenza, l'API Fetch riceve un argomento obbligatorio. Accetta  anche un argomento facoltativo, specialmente se si usa il metodo predefinito GET, rappresentato da un oggetto, tuttavia per gli altri metodi come POST e DELETE questo argomento è richiesto in quanto in esso va inserito il tipo di metodo da utilizzare:

fetch(url, {
    method: "GET" // predefinito, possiamo ignorarlo
})

Fino a qui abbiamo imparato come funzionano le cose, ora mettiamo tutto insieme ed eseguiamo una richiesta get per ottenere dati dalla nostra API.

Nuovamente, useremo l'API libera online JSONPlaceholder per recuperare un elenco di post per la nostra applicazione:

import React, { useState, useEffect } from 'react';

const App = () => {
   const [posts, setPosts] = useState([]);
   useEffect(() => {
      fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
         .then((response) => response.json())
         .then((data) => {
            console.log(data);
            setPosts(data);
         })
         .catch((err) => {
            console.log(err.message);
         });
   }, []);

return (
   // ... qui i dati vengono utilizzati
);
};

Nella parte di codice precedente abbiamo creato codice per conservare i dati che otteniamo dall'API così da poterli utilizzare più tardi nella nostra applicazione. Abbiamo anche impostato come valore predefinito un array vuoto.

const [posts, setPosts] = useState([]);

Il grosso dell'operazione si svolge nell'hook useEffect, in modo che i dati/post siano recuperati non appena l'applicazione viene caricata. La richiesta di recupero dati genera una promise, che possiamo accettare o rifiutare:

useEffect(() => {
   fetch('https://jsonplaceholder.typicode.com/posts?_limit=10').then(
      (response) => console.log(response)
   );
}, []);

Questa risposta contiene una grande quantità di dati, come il codice di stato, testo e altre informazioni che ci serviranno per gestire gli errori successivamente.

Fino ad ora, abbiamo gestito la risoluzione della promise con .then(), ma essa restituisce un oggetto Response, che non è quello che vogliamo. Dobbiamo estrarre dall'oggetto Response i dati nel formato JSON usando il metodo json(). Anche questo metodo restituisce una promise che possiamo utilizzare per ottenere i dati effettivi usando il secondo .then().

useEffect(() => {
   fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
      .then((response) => response.json())
      .then((data) => {
         console.log(data);
         setPosts(data);
      });
}, []);

Se diamo un'occhiata alla console, vedremo che abbiamo recuperato 10 post dalla nostra API, che abbiamo poi assegnato allo stato specificato in precedenza.

Non abbiamo ancora finito, in quanto abbiamo gestito solo la parte di risoluzione della promise, non la parte di rifiuto, che gestiremo tramite il metodo .catch() :

useEffect(() => {
   fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
      .then((response) => response.json())
      .then((data) => {
         console.log(data);
         setPosts(data);
      })
      .catch((err) => {
         console.log(err.message);
      });
}, []);

Fino a ora abbiamo visto come eseguire una richiesta GET, che può essere facilmente usata nella nostra applicazione eseguendo un loop sul nostro array:

const App = () => {
// ...

   return (
   <div className="posts-container">
      {posts.map((post) => {
         return (
            <div className="post-card" key={post.id}>
               <h2 className="post-title">{post.title}</h2>
               <p className="post-body">{post.body}</p>
               <div className="button">
               <div className="delete-btn">Delete</div>
               </div>
            </div>
         );
      })}
   </div>
   );
};

export default App;

Come Eseguire una Richiesta POST in React con l'API Fetch

Possiamo usare il metodo HTTP POST per inviare dati verso un endpoint. Funziona in modo simile alla richiesta GET, la differenza principale è che dobbiamo aggiungere il tipo di metodo e due parametri aggiuntivi all'oggetto che contiene i dati opzionali:

const addPosts = async (title, body) => {
await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: JSON.stringify({
   title: title,
   body: body,
   userId: Math.random().toString(36).slice(2),
}),
headers: {
   'Content-type': 'application/json; charset=UTF-8',
},
})
.then((response) => response.json())
.then((data) => {
   setPosts((posts) => [data, ...posts]);
   setTitle('');
   setBody('');
})
.catch((err) => {
   console.log(err.message);
});
};

I principali parametri che potrebbero apparire strani sono body e headers.

body contiene i dati che vogliamo passare all'API, che prima dobbiamo convertire in stringa JSON in quanto stiamo inviando dati a un server web. headers specifica il tipo di dati, che sarà sempre lo stesso quando si utilizzano API REST. Impostiamo anche lo stato per conservare i nuovi dati e distribuire i dati rimasti nell'array.

Osservando il metodo addPost() che abbiamo creato, ci attendiamo che questi dati provengano da un form o altro. Nel nostro caso, ho creato un form, ottenuto i dati del form tramite stati, quindi li ho aggiunti al metodo una volta che il form viene inviato:

import React, { useState, useEffect } from 'react';
const App = () => {
const [title, setTitle] = useState('');
const [body, setBody] = useState('');
// ...
const addPosts = async (title, body) => {
   await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',
      body: JSON.stringify({
         title: title,
         body: body,
         userId: Math.random().toString(36).slice(2),
      }),
      headers: {
         'Content-type': 'application/json; charset=UTF-8',
      },
   })
      .then((response) => response.json())
      .then((data) => {
         setPosts((posts) => [data, ...posts]);
         setTitle('');
         setBody('');
      })
      .catch((err) => {
         console.log(err.message);
      });
};

const handleSubmit = (e) => {
   e.preventDefault();
   addPosts(title, body);
};    

return (
   <div className="app">
      <div className="add-post-container">
         <form onSubmit={handleSubmit}>
            <input type="text" className="form-control" value={title}
               onChange={(e) => setTitle(e.target.value)}
            />
            <textarea name="" className="form-control" id="" cols="10" rows="8" 
               value={body} onChange={(e) => setBody(e.target.value)} 
            ></textarea>
            <button type="submit">Add Post</button>
         </form>
      </div>
      {/* ... */}
   </div>
);
};

export default App;

Come Eseguire una Richiesta DELETE in React con l'API Fetch

Puoi usare il metodo HTTP DELETE per eliminare dati da un endpoint. Funziona in modo simile alla richiesta GET, la differenza principale è l'aggiunta del tipo di metodo nell'oggetto opzionale:

const deletePost = async (id) => {
await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
   method: 'DELETE',
}).then((response) => {
   if (response.status === 200) {
      setPosts(
         posts.filter((post) => {
            return post.id !== id;
         })
      );
   } else {
      return;
   }
});
};

La richiesta viene attivata quando viene premuto il pulsante e otteniamo l'id dello specifico post relativo al bottone premuto. Poi eliminiamo quei dati da tutti i dati restituiti.

Ciò farà si che il post sia rimosso dall'API ma non immediatamente dall'interfaccia utente, ecco perché abbiamo aggiunto un filtro per eliminare anche i dati dall'applicazione. Per ciascun elemento del loop, il pulsante di cancellazione appare in questo modo:

const App = () => {
// ...

   return (
   <div className="posts-container">
      {posts.map((post) => {
         return (
            <div className="post-card" key={post.id}>
               {/* ... */}
               <div className="button">
                  <div className="delete-btn" onClick={() => deletePost(post.id)}>
                     Delete
                  </div>
               </div>    
            </div>
         );
      })}
   </div>
   );
};

export default App;

Come Usare Async/Await nell'API Fetch

Fino a ora abbiamo visto come eseguire richieste con Fetch normalmente, utilizzando la sintassi di promise, che a volte può confondere. Poi c'è il concatenamento. Possiamo evitare il concatenamento con .then() usando async/await per scrivere codice più leggibile.

Per usare async/await, prima si chiama async nella funzione. Poi quando facciamo una richiesta e ci attendiamo una risposta, aggiungiamo la parola chiave await davanti alla chiamata alla funzione dalla quale attendere fino a quando la promise è pronta con il risultato.

Quando usiamo async/await, tutte le nostre richieste Fetch saranno scritte in questo modo:

import React, { useState, useEffect } from 'react';

const App = () => {
   const [title, setTitle] = useState('');
   const [body, setBody] = useState('');
   const [posts, setPosts] = useState([]);

   // GET with fetch API
   useEffect(() => {
      const fetchPost = async () => {
         const response = await fetch(
            'https://jsonplaceholder.typicode.com/posts?_limit=10'
         );
         const data = await response.json();
         console.log(data);
         setPosts(data);
      };
      fetchPost();
   }, []);

   // Delete with fetchAPI
   const deletePost = async (id) => {
      let response = await fetch(
         `https://jsonplaceholder.typicode.com/posts/${id}`,
         {
            method: 'DELETE',
         }
      );
      if (response.status === 200) {
         setPosts(
            posts.filter((post) => {
               return post.id !== id;
            })
         );
      } else {
         return;
      }
   };

   // Post with fetchAPI
   const addPosts = async (title, body) => {
      let response = await fetch('https://jsonplaceholder.typicode.com/posts', {
         method: 'POST',
         body: JSON.stringify({
            title: title,
            body: body,
            userId: Math.random().toString(36).slice(2),
         }),
         headers: {
            'Content-type': 'application/json; charset=UTF-8',
         },
      });
      let data = await response.json();
      setPosts((posts) => [data, ...posts]);
      setTitle('');
      setBody('');
   };

   const handleSubmit = (e) => {
      e.preventDefault();
      addPosts(title, body);
   };

   return (
      // ...
   );
};

export default App;

Come Gestire gli Errori con l'API Fetch

In questa sezione, daremo un'occhiata a come gestire gli errori sia nel modo tradizionale che con async/await.

Possiamo usare i dati in risposta per gestire gli errori nell'API Fetch, oppure possiamo usare l'istruzione try/catch quando utilizziamo async/await.

Diamo un'occhiata a come possiamo farlo in genere con l'API Fetch:

const fetchPost = () => {
fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
   .then((response) => {
      if (!response.ok) {
         throw Error(response.statusText);
      }
      return response.json();
   })
   .then((data) => {
      console.log(data);
      setPosts(data);
   })
   .catch((err) => {
      console.log(err.message);
   });
};

Puoi trovare ulteriori informazioni sugli errori dell'API Fetch qui.

Per quanto riguarda async/await possiamo usare l'istruzione try / catch in questo modo:

const fetchPost = async () => {
   try {
      const response = await fetch(
         'https://jsonplaceholder.typicode.com/posts?_limit=10'
      );
      const data = await response.json();
      setPosts(data);
   } catch (error) {
      console.log(error);
   }
};

Come Utilizzare le API con Axios

Axios  è una libreria HTTP client basata sulle promise che facilita l'invio di richieste asincrone HTTP verso endpoint REST. Nel nostro caso l'endpoint è l'API dei Post JSONPlaceholder, alla quale invieremo richieste GET, POST, e DELETE.

Come Installare e Configurare una Istanza di Axios

Axios, al contrario dell'API Fetch, non è integrata, pertanto la dobbiamo includere nel nostro progetto per poterla usare.

Puoi aggiungere Axios al tuo progetto eseguendo il seguente comando:

npm install axios

Una volta installata con successo Axios, possiamo procedere alla creazione di una istanza, il che è opzionale ma consigliato in quanto ci risparmia inutili ripetizioni.

Per creare una istanza usiamo il metodo .create(), che possiamo utilizzare per specificare informazioni tipo l'URL e possibili header:

import axios from "axios";

const client = axios.create({
   baseURL: "https://jsonplaceholder.typicode.com/posts" 
});

Come Eseguire una Richiesta GET in React con Axios

Useremo l'istanza dichiarata in precedenza per eseguire una richiesta GET. Tutto quello che dovremo fare sarà impostare i parametri, se necessari, e ottenere una risposta JSON come formato predefinito.

Diversamente dal metodo dell'API Fetch, non sono necessarie opzioni per dichiarare il metodo. Semplicemente utilizziamo il tipo di richiesta come metodo dell'istanza ed eseguiamo la richiesta.

useEffect(() => {
   client.get('?_limit=10').then((response) => {
      setPosts(response.data);
   });
}, []);

Come Eseguire una Richiesta POST in React con Axios

Come detto prima, puoi usare il metodo  POST per inviare dati a un endpoint. Funziona in modo simile alla richiesta GET, la principale differenza è che occorre chiamare il relativo metodo passando come parametro un oggetto contenente i dati che vogliamo inviare:

const addPosts = (title, body) => {
   client
      .post('', {
         title: title,
         body: body,
      })
      .then((response) => {
         setPosts((posts) => [response.data, ...posts]);
      });
};

Come Eseguire una Richiesta DELETE in React con Axios

Possiamo eseguire richiese di cancellazione usando il metodo delete, che riceve un id ed elimina il post relativo dall'API. Useremo anche il metodo filter per eliminarlo anche dall'interfaccia utente, così come abbiamo fatto con il corrispondente metodo dell'API Fetch:

const deletePost = (id) => {
   client.delete(`${id}`);
   setPosts(
      posts.filter((post) => {
         return post.id !== id;
      })
   );
};

Come Usare Async/Await in Axios

Fino a ora abbiamo visto come effettuare richieste Axios usando la sintassi di promise. Ora vediamo come possiamo usare async/await per scrivere meno codice ed evitare il concatenamento con .then().

Quando usiamo async/await, tutte le nostre richieste Axios saranno così:

import React, { useState, useEffect } from 'react';

const App = () => {
   const [title, setTitle] = useState('');
   const [body, setBody] = useState('');
   const [posts, setPosts] = useState([]);

   // GET with Axios
   useEffect(() => {
      const fetchPost = async () => {
         let response = await client.get('?_limit=10');
         setPosts(response.data);
      };
      fetchPost();
   }, []);

   // Delete with Axios
   const deletePost = async (id) => {
      await client.delete(`${id}`);
      setPosts(
         posts.filter((post) => {
            return post.id !== id;
         })
      );
   };

   // Post with Axios
   const addPosts = async (title, body) => {
      let response = await client.post('', {
         title: title,
         body: body,
      });
      setPosts((posts) => [response.data, ...posts]);
   };

   const handleSubmit = (e) => {
      e.preventDefault();
      addPosts(title, body);
   };

   return (
      // ...
   );
};

export default App;

Come Gestire Errori con Axios

Per le richieste Axios basate su promise possiamo usare i metodi .then() e .catch(), mentre per async/await possiamo usare il blocco try...catch. È molto simile a come abbiamo implementato l'API Fetch, e il blocco try...catch è scritto in questo modo:

const fetchPost = async () => {
   try {
      let response = await client.get('?_limit=10');
      setPosts(response.data);
   } catch (error) {
      console.log(error);
   }
};

Puoi trovare ulteriori informazioni sulla gestione degli errori con Axios qui

API Fetch Contro Axios

Potresti aver notato alcune differenze, ora le metteremo in una pratica tabella in modo da confrontare correttamente  Fetch e Axios.

Queste distinzioni ti aiuteranno a decidere quale libreria utilizzare per un progetto specifico. Tra queste distinzioni ci sono:

AXIOS FETCH
Axios è un pacchetto a sé stante di terze parti semplice da installare. Fetch è integrato nella maggior parte dei browser più moderni. Non è richiesta installazione.
Axios usa la proprietà data. Fetch usa la proprietà body.
I dati da Axios contengono l'oggetto. Il contenuto della proprietà body di Fetch deve essere convertito in stringa.
Quando status è 200 statusText è 'OK,' la richiesta Axios risulta accettata. La richiesta Fetch viene accettata quando l'oggetto in risposta contiene la proprietà ok.
Axios esegue una conversione dei dati in JSON automaticamente. Fetch ha un processo a due fasi per gestire i dati JSON- prima si esegue l'effettiva richiesta; poi si chiama il metodo .json() sulla risposta.
Axios consente la cancellazione e il timeout della richiesta Fetch no
Axios ha supporto integrato per l'avanzamento del download. Fetch non supporta l'avanzamento del download.
Axios è ha un ampio supporto per i browser. Fetch è compatibile solo con Chrome 42+, Firefox 39+, Edge 14+, e Safari 10.1+ (ciò è noto come retrocompatibilità).

Conclusione

In questa guida, abbiamo imparato come utilizzare le API REST in React usando l'API Fetch oppure Axios.

Ti aiuterà a iniziare a usare le API in React e da lì, sarai in grado di utilizzare i dati in modi più complessi e manipolare le tue API come preferisci.