Frontend

Come si fa il fetch dei dati in React?

Autore

Manuel Ricci

Nell'ambito dello sviluppo web contemporaneo, l'efficienza e la dinamicità nel recupero dei dati sono essenziali per garantire esperienze utente fluide e immediate. React, che si distingue tra le librerie JavaScript per la realizzazione di interfacce utente, mette a disposizione una varietà di strumenti e modelli progettuali specificamente ideati per agevolare il fetching dei dati.

In questo contesto, voglio esplorare le principali metodologie impiegate nel fetching dei dati all'interno di applicazioni React. Saranno illustrati l'utilizzo della Fetch API per effettuare richieste HTTP, l'impiego dell'hook useEffect per la gestione di effetti collaterali, le strategie di gestione degli stati utili a monitorare le fasi di caricamento, l'eventuale presenza di errori e la ricezione dei dati, fino all'introduzione di SWR come avanzata tecnica di fetching dati, che promette di ottimizzare ulteriormente queste operazioni.

Fetch API e useEffect

Il punto di partenza per il fetching dei dati in un'applicazione React è la Fetch API del browser, che permette di effettuare richieste HTTP asincrone in JavaScript. Combinata con l'hook useEffect di React, che consente di eseguire effetti collaterali in componenti funzionali, possiamo facilmente orchestrare il fetching dei dati all'avvio del componente o in risposta a cambiamenti di stato o prop.

La Fetch API fornisce un'interfaccia JavaScript per l'accesso e la manipolazione di parti della pipeline HTTP, come le richieste e le risposte. Questa API offre una definizione più potente e flessibile per le operazioni di rete. Rispetto a XMLHttpRequest, è più semplice e più potente. Ne ho già parlato ampiamente durante il corso di JavaScript.

Per eseguire una chiamata HTTP utilizzando la Fetch API, si può utilizzare il metodo fetch(), che accetta il primo parametro come URL della risorsa che si desidera recuperare e un oggetto opzionale di opzioni per personalizzare la richiesta HTTP. Ritorna una promessa che risolve in un oggetto Response. Come si fa però ad usare fetch con React?

Uso di useEffect per il fetching dei dati

useEffect è un hook che permette di eseguire effetti collaterali in componenti funzionali. È comunemente usato per il fetching di dati, sottoscrizioni, o manualmente cambiare il DOM in React components. Lo abbiamo già incontrato nella lezione sugli hook.

Quando si utilizza useEffect per il fetching di dati, si colloca la chiamata alla Fetch API all'interno della funzione passata a useEffect. Ciò assicura che la chiamata alla Fetch API venga eseguita dopo che il componente è stato montato. Per evitare chiamate API ripetute o infinite, è importante includere un array di dipendenze vuoto come secondo argomento di useEffect, indicando che l'effetto non dipende da alcuna prop o stato e quindi dovrebbe eseguire solo una volta.

In questo esempio, utilizzeremo la Fetch API per recuperare i dati da un'API fittizia e li visualizzeremo in un componente. Questo esempio si concentrerà sull'uso di useEffect per effettuare il fetch dei dati al caricamento del componente e sull'uso delle interfacce TypeScript per definire il tipo di dati atteso dalla risposta.

Per semplificare, non gestiremo gli stati di caricamento o errore, ma ti mostrerò come potresti fare per estendere l'esempio.

Definiamo prima l'interfaccia per i dati che ci aspettiamo di ricevere:

1import React from 'react';
2
3interface Todo {
4  userId: number,
5  id: number,
6  title: string,
7  completed: boolean
8}
9
10const TodoList: React.FC = () => {
11  const [todos, setTodos] = React.useState<Todo[]>([]);
12
13  React.useEffect(() => {
14    const fetchData = async() => {
15      const response = await fetch('https://jsonplaceholder.typicode.com/todos');
16      const data = await response.json();
17      setTodos(data);
18    };
19    fetchData().catch(console.error);
20  }, []);
21
22  return (
23    <div>
24      <h1>Todo List</h1>
25      <ul>
26        {todos.map(todo => (
27          <li key={todo.id} style={{textDecoration: todo.completed ? 'line-through' : 'none'}}>
28            {todo.title}
29          </li>
30        ))}
31      </ul>
32    </div>
33  );
34}
35
36export default TodoList;

In questo esempio:

  • Abbiamo definito un'interfaccia Todo per descrivere la struttura dei dati del todo che ci aspettiamo di ricevere dall'API.
  • Abbiamo creato un componente TodoList che:
    • Utilizza lo stato todos per memorizzare l'elenco degli utenti recuperati.
    • Utilizza l'hook useEffect per effettuare il fetch dei dati quando il componente viene montato. L'array di dipendenze vuoto ([]) assicura che l'effetto venga eseguito solo una volta dopo il primo rendering.
    • Definisce una funzione asincrona fetchData all'interno dell'hook useEffect, che effettua il fetch dei dati e aggiorna lo stato todos con i dati ricevuti.
    • Visualizza l'elenco dei todo utilizzando il metodo map per iterare su ogni elemento dell'array todos e restituire un elemento della lista per ciascun utente.

Così però è poco user friendly, possiamo migliorare l’esempio con la gestione degli stati di caricamento.

Gestione degli Stati

La gestione degli stati di caricamento, errore e dati è cruciale per informare l'utente sullo stato della richiesta di dati. Utilizzando gli hook di stato di React, come useState, possiamo tracciare queste condizioni e rendere la nostra UI reattiva a cambiamenti nel ciclo di vita del fetching dei dati.

Aggiungiamo alla nostra implementazione precedente la gestione degli stati di caricamento per fornire feedback visivo all'utente durante il recupero dei dati. Questo è particolarmente utile in ambienti reali dove le risposte dell'API possono richiedere un po' di tempo.

Per gestire lo stato di caricamento, aggiungeremo due nuovi stati isLoading e error che ci permetterà di sapere quando i dati stanno venendo caricati, quando il caricamento è completato o se ci sono stati degli errori. Vediamo come si modifica l'esempio:

1import React, { useEffect, useState } from 'react';
2
3interface Todo {
4  userId: number,
5  id: number,
6  title: string,
7  completed: boolean
8}
9
10const TodoList: React.FC = () => {
11  const [todos, setTodos] = useState<Todo[]>( [] );
12  const [isLoading, setIsLoading] = useState<boolean>( true );
13  const [error, setError] = useState<string | null>( null );
14
15  useEffect( () => {
16    const fetchData = async () => {
17      setIsLoading( true );
18      setError( null );
19      try {
20        const response = await fetch( 'https://jsonplaceholder.typicode.com/todos' );
21        if ( !response.ok ) {
22          throw new Error( 'Network response was not ok' );
23        }
24        const data = await response.json();
25        setTodos( data );
26      } catch ( error: any ) {
27        setError( error.message || 'Errore sconosciuto' );
28      } finally {
29        setIsLoading( false );
30      }
31    };
32    fetchData();
33  }, [] );
34
35  return (
36    <div>
37      <h1>Todo List</h1>
38      {isLoading ? (
39        <p>Loading...</p>
40      ) : error ? (
41        <p>Error: {error}</p>
42      ) : (
43        <ul>
44          {todos.map( ( todo ) => (
45            <li key={todo.id} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }} >
46              {todo.title}
47            </li>
48          ) )}
49        </ul>
50      )}
51    </div>
52  );
53}
54
55export default TodoList;

Questo approccio migliora l'esperienza utente indicando chiaramente quando i dati sono in fase di caricamento e gestendo in modo elegante sia il successo che il fallimento del fetch dei dati.

Nell'esempio sopra, abbiamo introdotto tre stati utilizzando lo useState hook: todos, loading, e error. Questi stati aiutano a gestire le diverse fasi del ciclo di vita di una richiesta HTTP in un componente React:

  • Caricamento (loading): Inizialmente impostato su true, questo stato indica che la richiesta è in corso. Puoi utilizzarlo per mostrare un indicatore di caricamento all'utente.
  • Errore (error): Se la richiesta fallisce per qualsiasi motivo (ad esempio, problemi di rete, risposte errate dal server), questo stato viene utilizzato per catturare e mostrare un messaggio di errore.
  • Todos (todos): Una volta che la richiesta ha successo, i dati recuperati vengono salvati in questo stato. Puoi poi utilizzarlo per visualizzare i dati all'interno del tuo componente.

Questo approccio consente di creare un'esperienza utente fluida e informativa durante l'interazione con le API web in un'applicazione React.

SWR: Stale While Revalidate

Per un approccio più sofisticato al fetching dei dati, introduciamo SWR, una libreria per React che implementa il pattern "stale-while-revalidate".

SWR fornisce una soluzione elegante per il caching dei dati, la riconvalida in background, e molte altre funzionalità avanzate che aiutano a migliorare le prestazioni e l'esperienza utente delle applicazioni web. Grazie alla sua API semplice ma potente, SWR semplifica il processo di fetching dei dati, riducendo la complessità e il codice boilerplate.

SWR è una libreria creata dal team di Vercel che si basa sull'idea di "stale-while-revalidate" (vecchio mentre convalida), un approccio HTTP caching che permette di mostrare prima i dati cache (stale), poi di fare il fetch dei dati aggiornati (revalidate), e infine di aggiornare l'interfaccia con i dati freschi. SWR fornisce una serie di funzionalità per migliorare l'esperienza di fetching dei dati nelle applicazioni web, come caching automatico, re-fetching in background, e molto altro.

Caratteristiche principali di SWR:

  • Data Fetching senza configurazione: SWR gestisce automaticamente il caching, la convalida in background, e il mantenimento dello stato, riducendo la necessità di codice boilerplate.
  • Re-fetching intelligente: SWR re-fetcha i dati automaticamente quando l'utente riporta in primo piano l'app, quando si recupera la connessione di rete, o a intervalli di tempo configurabili.
  • UI sempre reattiva: Grazie al suo sistema di caching e aggiornamento, SWR permette di avere un'interfaccia utente che rimane reattiva e veloce, mostrando dati immediatamente e aggiornandoli in background.
  • Semplice ma potente API: SWR offre un'API semplice da usare, ma estremamente configurabile e estensibile per adattarsi a casi d'uso avanzati.

Esempio di utilizzo di SWR:

Per iniziare con SWR, prima è necessario installare la libreria:

1npm install swr

Ecco un esempio di come si può utilizzare SWR per fetchare i dati di un utente da un'API:

1import React from 'react';
2import useSWR from 'swr';
3
4interface Todo {
5  userId: number,
6  id: number,
7  title: string,
8  completed: boolean
9}
10
11const fetcher = ( url: string ) => fetch( url ).then( ( res ) => res.json() );
12
13const TodoList: React.FC = () => {
14  const { data: todos, error } = useSWR<Todo[]>( 'https://jsonplaceholder.typicode.com/todos', fetcher );
15
16  if ( error ) return <div>Failed to load</div>;
17  if ( !todos ) return <div>Loading...</div>;
18  return (
19    <ul>
20      { todos.map( ( todo ) => (
21        <li key={ todo.id } style={ { textDecoration: todo.completed ? 'line-through' : 'none' } }>
22          { todo.title }
23        </li>
24      ) ) }
25    </ul>
26  )
27}
28
29export default TodoList;

Nell'esempio, useSWR accetta due parametri: la chiave di cache (in questo caso, l'URL dell'API) e una funzione fetcher che definisce come recuperare i dati (in questo esempio, usando la Fetch API standard per ottenere i dati in formato JSON). SWR utilizza la chiave di cache per de-duplicare le richieste e condividere i dati tra componenti, ottimizzando le performance e riducendo il carico sul server.

Qualche informazioni in più su SWR

  • Gestione degli errori: SWR fornisce un modo semplice per gestire gli errori attraverso lo stato error restituito dal hook useSWR.
  • Convalida iniziale e di background: SWR esegue automaticamente la convalida dei dati all'avvio dell'applicazione e in background per mantenere i dati aggiornati senza interrompere l'esperienza dell'utente.
  • Configurazioni globali e locali: SWR consente di definire configurazioni sia a livello globale che locale, permettendo di personalizzare il comportamento del fetching in base alle necessità specifiche del componente o dell'applicazione.
  • Plugin e middleware: SWR supporta plugin e middleware che estendono le sue funzionalità, come la possibilità di aggiungere funzionalità di retry automatico o integrazioni con altre librerie di gestione dello stato.

SWR rappresenta una soluzione moderna e efficiente per la gestione del fetching dei dati nelle applicazioni React, offrendo un approccio ottimizzato e basato sulle performance senza sacrificare la flessibilità o la potenza.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.