Comprensione di GraphQl lato client con Apollo-Client nelle app React

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Hai mai provato a interagire con un server GraphQL in un'applicazione lato client e hai sentito di arrenderti anche prima di arrivare da nessuna parte? Hai mai rifiutato un invito a partecipare a una base di codice che richiede l'utilizzo dell'API GraphQL perché non ne avevi idea? Ti sei mai sentito l'unico ingegnere front-end che non ha imparato a utilizzare le API GraphQL? Se hai risposto sì a una di queste domande, allora questo tutorial è per te. Daremo un'occhiata più da vicino ad alcune nozioni di base di GraphQL e Apollo Client, oltre a come lavorare con entrambi. Alla fine, avremo creato un'app per negozi di animali che utilizza Apollo Client. Quindi, puoi continuare a costruire il tuo prossimo progetto.

Secondo State of JavaScript 2019, il 38,7% degli sviluppatori vorrebbe utilizzare GraphQL, mentre il 50,8% degli sviluppatori vorrebbe imparare GraphQL.

Essendo un linguaggio di query, GraphQL semplifica il flusso di lavoro della creazione di un'applicazione client. Elimina la complessità della gestione degli endpoint API nelle app lato client perché espone un singolo endpoint HTTP per recuperare i dati richiesti. Quindi, elimina l'overfetching e il underfetch dei dati, come nel caso di REST.

Ma GraphQL è solo un linguaggio di query. Per usarlo facilmente, abbiamo bisogno di una piattaforma che faccia il lavoro pesante per noi. Una di queste piattaforme è Apollo.

La piattaforma Apollo è un'implementazione di GraphQL che trasferisce i dati tra il cloud (il server) all'interfaccia utente della tua app. Quando si utilizza Apollo Client, tutta la logica per il recupero dei dati, il monitoraggio, il caricamento e l'aggiornamento dell'interfaccia utente è incapsulata useQuery (come nel caso di React). Quindi, il recupero dei dati è dichiarativo. Ha anche una memorizzazione nella cache di configurazione zero. Semplicemente impostando Apollo Client nella tua app, ottieni una cache intelligente pronta all'uso, senza alcuna configurazione aggiuntiva richiesta.

Apollo Client è anche interoperabile con altri framework, come Angular, Vue.js e React.

Nota : questo tutorial andrà a beneficio di coloro che hanno lavorato con RESTful o altre forme di API in passato sul lato client e vogliono vedere se vale la pena provare GraphQL. Ciò significa che avresti dovuto lavorare con un'API prima; solo allora sarai in grado di capire quanto potrebbe essere vantaggioso GraphQL per te. Mentre tratteremo alcune nozioni di base di GraphQL e Apollo Client, una buona conoscenza di JavaScript e React Hooks tornerà utile.

Nozioni di base su GraphQL

Questo articolo non è un'introduzione completa a GraphQL, ma definiremo alcune convenzioni prima di continuare.

Che cos'è GraphQL?

GraphQL è una specifica che descrive un linguaggio di query dichiarativo che i tuoi clienti possono utilizzare per chiedere a un'API i dati esatti che desiderano. Ciò si ottiene creando uno schema di tipo forte per la tua API, con la massima flessibilità. Garantisce inoltre che l'API risolva i dati e che le query client vengano convalidate rispetto a uno schema. Questa definizione significa che GraphQL contiene alcune specifiche che lo rendono un linguaggio di query dichiarativo, con un'API tipizzata in modo statico (costruita attorno a Typescript) e che consente al client di sfruttare quei sistemi di tipi per chiedere all'API i dati esatti che desidera .

Quindi, se abbiamo creato alcuni tipi con alcuni campi al loro interno, dal lato client potremmo dire "Dacci questi dati con questi campi esatti". Quindi l'API risponderà con quella forma esatta, proprio come se stessimo utilizzando un sistema di tipi in un linguaggio fortemente tipizzato. Puoi saperne di più nel mio articolo dattiloscritto.

Diamo un'occhiata ad alcune convenzioni di GraphQl che ci aiuteranno mentre continuiamo.

Le basi

  • Operazioni
    In GraphQL, ogni azione eseguita è chiamata operazione. Ci sono alcune operazioni, vale a dire:
    • Domanda
      Questa operazione riguarda il recupero dei dati dal server. Potresti anche chiamarlo recupero di sola lettura.
    • Mutazione
      Questa operazione comporta la creazione, l'aggiornamento e l'eliminazione di dati da un server. Viene comunemente chiamata operazione CUD (creazione, aggiornamento ed eliminazione).
    • Abbonamenti
      Questa operazione in GraphQL comporta l'invio di dati da un server ai suoi client quando si verificano eventi specifici. Di solito sono implementati con WebSocket.

In questo articolo ci occuperemo solo delle operazioni di query e di mutazione.

  • Nomi delle operazioni
    Sono disponibili nomi univoci per le operazioni di query e mutazione lato client.
  • Variabili e argomenti
    Le operazioni possono definire argomenti, proprio come una funzione nella maggior parte dei linguaggi di programmazione. Tali variabili possono quindi essere passate a query o chiamate di mutazione all'interno dell'operazione come argomenti. Le variabili dovrebbero essere fornite in fase di esecuzione durante l'esecuzione di un'operazione dal client.
  • alias
    Questa è una convenzione in GraphQL lato client che implica la ridenominazione di nomi di campo dettagliati o vaghi con nomi di campo semplici e leggibili per l'interfaccia utente. L'alias è necessario nei casi d'uso in cui non si desidera avere nomi di campo in conflitto.
Convenzioni di base di GraphQL
Convenzioni di base di GraphQL. (Grande anteprima)
Altro dopo il salto! Continua a leggere sotto ↓

Che cos'è GraphQL lato client?

Quando un ingegnere front-end crea componenti dell'interfaccia utente utilizzando qualsiasi framework, come Vue.js o (nel nostro caso) React, tali componenti vengono modellati e progettati in base a un determinato modello sul client per adattarsi ai dati che verranno recuperati dal server.

Uno dei problemi più comuni con le API RESTful è l'overfetch e l'underfetch. Ciò accade perché l'unico modo in cui un client può scaricare i dati è colpire gli endpoint che restituiscono strutture di dati fisse . L'overfetching in questo contesto significa che un client scarica più informazioni di quelle richieste dall'app.

In GraphQL, d'altra parte, invierai semplicemente una singola query al server GraphQL che include i dati richiesti. Il server risponderebbe quindi con un oggetto JSON dei dati esatti che hai richiesto, quindi nessun overfetching. Sebastian Eschweiler spiega le differenze tra le API RESTful e GraphQL.

GraphQL lato client è un'infrastruttura lato client che si interfaccia con i dati di un server GraphQL per eseguire le seguenti funzioni:

  • Gestisce i dati inviando query e modificando i dati senza che tu debba costruire richieste HTTP da solo. Puoi dedicare meno tempo all'impianto idraulico dei dati e più tempo alla creazione dell'applicazione effettiva.
  • Gestisce la complessità di una cache per te. Quindi, puoi archiviare e recuperare i dati recuperati dal server, senza alcuna interferenza di terze parti, ed evitare facilmente di recuperare risorse duplicate. Pertanto, identifica quando due risorse sono uguali, il che è ottimo per un'app complessa.
  • Mantiene la tua interfaccia utente coerente con Optimistic UI, una convenzione che simula i risultati di una mutazione (cioè i dati creati) e aggiorna l'interfaccia utente anche prima di ricevere una risposta dal server. Una volta ricevuta la risposta dal server, il risultato ottimistico viene buttato via e sostituito con il risultato effettivo.

Per ulteriori informazioni su GraphQL lato client, risparmia un'ora con il cocreatore di GraphQL e altre persone interessanti su GraphQL Radio.

Cos'è il client Apollo?

Apollo Client è un client GraphQL interoperabile, ultraflessibile e guidato dalla comunità per JavaScript e piattaforme native. Le sue straordinarie funzionalità includono un robusto strumento di gestione dello stato (Apollo Link), un sistema di memorizzazione nella cache senza configurazione, un approccio dichiarativo al recupero dei dati, un'impaginazione facile da implementare e l'interfaccia utente ottimistica per l'applicazione lato client.

Apollo Client memorizza non solo lo stato dei dati prelevati dal server, ma anche lo stato che ha creato localmente sul tuo client; quindi, gestisce lo stato sia per i dati API che per i dati locali.

È anche importante notare che puoi utilizzare Apollo Client insieme ad altri strumenti di gestione dello stato, come Redux, senza conflitti. Inoltre, è possibile migrare la gestione dello stato, ad esempio, da Redux ad Apollo Client (che va oltre lo scopo di questo articolo). In definitiva, lo scopo principale di Apollo Client è consentire agli ingegneri di interrogare i dati in un'API senza problemi.

Caratteristiche del client Apollo

Apollo Client ha conquistato così tanti ingegneri e aziende grazie alle sue funzionalità estremamente utili che rendono la creazione di applicazioni moderne e robuste un gioco da ragazzi. Le seguenti caratteristiche sono integrate:

  • Memorizzazione nella cache
    Apollo Client supporta la memorizzazione nella cache al volo.
  • Interfaccia utente ottimista
    Apollo Client ha un ottimo supporto per l'interfaccia utente ottimistica. Implica la visualizzazione temporanea dello stato finale di un'operazione (mutazione) mentre l'operazione è in corso. Una volta completata l'operazione, i dati reali sostituiscono i dati ottimistici.
  • Impaginazione
    Apollo Client ha funzionalità integrate che semplificano l'implementazione dell'impaginazione nell'applicazione. Si occupa della maggior parte dei mal di testa tecnici relativi al recupero di un elenco di dati, sia in patch che in una volta, utilizzando la funzione fetchMore , fornita con l'hook useQuery .

In questo articolo, esamineremo una selezione di queste funzionalità.

Basta con la teoria. Stringi la cintura di sicurezza e prendi una tazza di caffè per accompagnare i tuoi pancake, mentre ci sporchiamo le mani.

Costruire la nostra app Web

Questo progetto è ispirato da Scott Moss.

Costruiremo una semplice app web per negozi di animali, le cui caratteristiche includono:

  • recuperare i nostri animali domestici dal lato server;
  • creazione di un animale domestico (che implica la creazione del nome, del tipo di animale domestico e dell'immagine);
  • utilizzando l'interfaccia utente ottimistica;
  • usando l'impaginazione per segmentare i nostri dati.

Per iniziare, clona il repository, assicurandoti che il ramo starter sia quello che hai clonato.

Iniziare

  • Installa l'estensione Apollo Client Developer Tools per Chrome.
  • Utilizzando l'interfaccia della riga di comando (CLI), accedere alla directory del repository clonato ed eseguire il comando per ottenere tutte le dipendenze: npm install .
  • Esegui il comando npm run app per avviare l'app.
  • Mentre sei ancora nella cartella principale, esegui il comando npm run server . Questo avvierà il nostro server back-end per noi, che useremo mentre procediamo.

L'app dovrebbe aprirsi in una porta configurata. Il mio è https://localhost:1234/ ; il tuo è probabilmente qualcos'altro.

Se tutto ha funzionato bene, la tua app dovrebbe assomigliare a questa:

Interfaccia utente del ramo di avviamento clonato
Interfaccia utente del ramo di avviamento clonato. (Grande anteprima)

Noterai che non abbiamo animali da mostrare. Questo perché non abbiamo ancora creato tale funzionalità.

Se hai installato correttamente gli strumenti per sviluppatori client Apollo, apri gli strumenti per sviluppatori e fai clic sull'icona nella barra delle applicazioni. Vedrai "Apollo" e qualcosa del genere:

Strumenti per sviluppatori client Apollo
Strumenti per sviluppatori client Apollo. (Grande anteprima)

Come gli strumenti per sviluppatori Redux e React, utilizzeremo gli strumenti per sviluppatori client Apollo per scrivere e testare le nostre query e mutazioni. L'estensione viene fornita con GraphQL Playground.

Recupero di animali domestici

Aggiungiamo la funzionalità che recupera gli animali domestici. Passa a client/src/client.js . Scriveremo il client Apollo, lo collegheremo a un'API, lo esporteremo come client predefinito e scriveremo una nuova query.

Copia il codice seguente e incollalo in client.js :

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' const link = new HttpLink({ uri: 'https://localhost:4000/' }) const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

Ecco una spiegazione di ciò che sta accadendo sopra:

  • ApolloClient
    Questa sarà la funzione che avvolge la nostra app e, quindi, si interfaccia con l'HTTP, memorizza nella cache i dati e aggiorna l'interfaccia utente.
  • InMemoryCache
    Questo è l'archivio dati normalizzato in Apollo Client che aiuta a manipolare la cache nella nostra applicazione.
  • HttpLink
    Questa è un'interfaccia di rete standard per modificare il flusso di controllo delle richieste GraphQL e recuperare i risultati GraphQL. Agisce come middleware, recuperando i risultati dal server GraphQL ogni volta che viene attivato il collegamento. Inoltre, è un buon sostituto per altre opzioni, come Axios e window.fetch .
  • Dichiariamo una variabile di collegamento assegnata a un'istanza di HttpLink . Richiede una proprietà uri e un valore al nostro server, che è https://localhost:4000/ .
  • La prossima è una variabile cache che contiene la nuova istanza di InMemoryCache .
  • La variabile client accetta anche un'istanza di ApolloClient e racchiude il link e la cache .
  • Infine, esportiamo il client in modo da poterlo utilizzare nell'applicazione.

Prima di vederlo in azione, dobbiamo assicurarci che la nostra intera app sia esposta ad Apollo e che la nostra app possa ricevere i dati recuperati dal server e che possa mutare tali dati.

Per raggiungere questo obiettivo, andiamo su client/src/index.js :

 import React from 'react' import ReactDOM from 'react-dom' import { BrowserRouter } from 'react-router-dom' import { ApolloProvider } from '@apollo/react-hooks' import App from './components/App' import client from './client' import './index.css' const Root = () => ( <BrowserRouter>
 <ApolloProvider client={client}> <App /> </ApolloProvider>
 </BrowserRouter> ); ReactDOM.render(<Root />, document.getElementById('app')) if (module.hot) { module.hot.accept() }

Come noterai nel codice evidenziato, abbiamo inserito il componente App in ApolloProvider e passato il client come prop al client . ApolloProvider è simile a Context.Provider di React. Avvolge la tua app React e colloca il client nel contesto, il che ti consente di accedervi da qualsiasi punto dell'albero dei componenti.

Per recuperare i nostri animali domestici dal server, dobbiamo scrivere query che richiedano i campi esatti che desideriamo. Vai su client/src/pages/Pets.js e copia e incolla il seguente codice al suo interno:

 import React, {useState} from 'react' import gql from 'graphql-tag' import { useQuery, useMutation } from '@apollo/react-hooks' import PetsList from '../components/PetsList' import NewPetModal from '../components/NewPetModal' import Loader from '../components/Loader'

const GET_PETS = gql` query getPets { pets { id name type img } } `;

export default function Pets () { const [modal, setModal] = useState(false)
 const { loading, error, data } = useQuery(GET_PETS); if (loading) return <Loader />; if (error) return <p>An error occured!</p>;


 const onSubmit = input => { setModal(false) } if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section>
 <PetsList pets={data.pets}/>
 </section> </div> ) }

Con pochi bit di codice, siamo in grado di recuperare gli animali domestici dal server.

Cos'è gql?

È importante notare che le operazioni in GraphQL sono generalmente oggetti JSON scritti con graphql-tag e con backtick.

I tag gql sono tag letterali del modello JavaScript che analizzano le stringhe di query GraphQL in GraphQL AST (albero della sintassi astratta).

  • Operazioni di interrogazione
    Per recuperare i nostri animali domestici dal server, dobbiamo eseguire un'operazione di query.
    • Poiché stiamo eseguendo un'operazione di query , era necessario specificare il type di operazione prima di denominarla.
    • Il nome della nostra query è GET_PETS . È una convenzione di denominazione di GraphQL per utilizzare camelCase per i nomi dei campi.
    • Il nome dei nostri campi è pets . Quindi, specifichiamo i campi esatti di cui abbiamo bisogno dal server (id, name, type, img) .
    • useQuery è un hook React che è la base per l'esecuzione di query in un'applicazione Apollo. Per eseguire un'operazione di query nel nostro componente React, chiamiamo l'hook useQuery , che è stato inizialmente importato da @apollo/react-hooks . Successivamente, gli passiamo una stringa di query GraphQL, che nel nostro caso è GET_PETS .
  • Quando il nostro componente esegue il rendering, useQuery restituisce una risposta dell'oggetto da Apollo Client che contiene il caricamento, l'errore e le proprietà dei dati. Pertanto, vengono destrutturati, in modo da poterli utilizzare per il rendering dell'interfaccia utente.
  • useQuery è fantastico. Non è necessario includere async-await . È già stato curato in background. Abbastanza bello, vero?
    • loading
      Questa proprietà ci aiuta a gestire lo stato di caricamento dell'applicazione. Nel nostro caso, restituiamo un componente Loader durante il caricamento della nostra applicazione. Per impostazione predefinita, il caricamento è false .
    • error
      Per ogni evenienza, utilizziamo questa proprietà per gestire qualsiasi errore che potrebbe verificarsi.
    • data
      Questo contiene i nostri dati effettivi dal server.
    • Infine, nel nostro componente PetsList , passiamo gli oggetti di scena degli pets , con data.pets come valore dell'oggetto.

A questo punto, abbiamo interrogato con successo il nostro server.

Per avviare la nostra applicazione, eseguiamo il seguente comando:

  • Avvia l'app client. Esegui il comando npm run app nella CLI.
  • Avvia il server. Eseguire il comando npm run server in un'altra CLI.
VScode CLI partizionata per avviare sia il client che il server.
VScode CLI partizionata per avviare sia il client che il server. (Grande anteprima)

Se tutto è andato bene, dovresti vedere questo:

Animali interrogati dal server.
Animali interrogati dal server.

Dati mutanti

Modificare i dati o creare dati in Apollo Client è quasi lo stesso che eseguire query sui dati, con lievi modifiche.

Sempre in client/src/pages/Pets.js , copiamo e incolliamo il codice evidenziato:

 .... const GET_PETS = gql` query getPets { pets { id name type img } } `;

const NEW_PETS = gql` mutation CreateAPet($newPet: NewPetInput!) { addPet(input: $newPet) { id name type img } } `;

 const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS);
 const onSubmit = input => { setModal(false)
 createPet({ variables: { newPet: input } }); } if (loading || newPet.loading) return <Loader />; if (error || newPet.error) return <p>An error occured</p>;
  
 if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section> <PetsList pets={data.pets}/> </section> </div> ) } export default Pets

Per creare una mutazione, faremmo i seguenti passi.

1. mutation

Per creare, aggiornare o eliminare, è necessario eseguire l'operazione di mutation . L'operazione di mutation ha un nome CreateAPet , con un argomento. Questo argomento ha una variabile $newPet , con un tipo di NewPetInput . Il ! significa che l'operazione è necessaria; quindi, GraphQL non eseguirà l'operazione a meno che non passiamo una variabile newPet cui tipo è NewPetInput .

2. addPet

La funzione addPet , che si trova all'interno dell'operazione di mutation , accetta un argomento di input ed è impostata sulla nostra variabile $newPet . I set di campi specificati nella nostra funzione addPet devono essere uguali ai set di campi nella nostra query. Gli insiemi di campo nella nostra operazione sono:

  • id
  • name
  • type
  • img

3. useMutation

L'hook useMutation React è l'API principale per l'esecuzione di mutazioni in un'applicazione Apollo. Quando abbiamo bisogno di mutare i dati, chiamiamo useMutation in un componente React e gli passiamo una stringa GraphQL (nel nostro caso, NEW_PETS ).

Quando il nostro componente esegue il rendering useMutation , restituisce una tupla (ovvero un insieme ordinato di dati che costituisce un record) in un array che include:

  • una funzione mutata che possiamo chiamare in qualsiasi momento per eseguire la mutate ;
  • un oggetto con campi che rappresentano lo stato attuale dell'esecuzione della mutazione.

useMutation viene passata una stringa di mutazione GraphQL (che nel nostro caso è NEW_PETS ). Abbiamo destrutturato la tupla, che è la funzione ( createPet ) che muterà i dati e il campo dell'oggetto ( newPets ).

4. createPet

Nella nostra funzione onSubmit , poco dopo lo stato setModal , abbiamo definito il nostro createPet . Questa funzione accetta una variable con una proprietà dell'oggetto di un valore impostato su { newPet: input } . L' input rappresenta i vari campi di input nel nostro modulo (come nome, tipo, ecc.).

Fatto ciò, il risultato dovrebbe assomigliare a questo:

Mutazione senza aggiornamento istantaneo
Mutazione senza aggiornamento istantaneo.

Se osservi la GIF da vicino, noterai che il nostro animale domestico creato non viene visualizzato all'istante, solo quando la pagina viene aggiornata. Tuttavia, è stato aggiornato sul server.

La grande domanda è: perché il nostro animale domestico non si aggiorna all'istante? Scopriamolo nella prossima sezione.

Memorizzazione nella cache nel client Apollo

Il motivo per cui la nostra app non si aggiorna automaticamente è che i nostri dati appena creati non corrispondono ai dati della cache in Apollo Client. Quindi, c'è un conflitto su cosa esattamente deve essere aggiornato dalla cache.

In poche parole, se eseguiamo una mutazione che aggiorna o elimina più voci (un nodo), allora siamo responsabili dell'aggiornamento di tutte le query che fanno riferimento a quel nodo, in modo che modifichi i nostri dati memorizzati nella cache in modo che corrispondano alle modifiche apportate da una mutazione al nostro back- dati finali .

Mantenere la cache sincronizzata

Esistono alcuni modi per mantenere sincronizzata la nostra cache ogni volta che eseguiamo un'operazione di mutazione.

Il primo consiste nel recuperare le query corrispondenti dopo una mutazione, utilizzando la proprietà dell'oggetto refetchQueries (il modo più semplice).

Nota: se dovessimo utilizzare questo metodo, richiederebbe una proprietà dell'oggetto nella nostra funzione createPet chiamata refetchQueries e conterrebbe un array di oggetti con un valore della query: refetchQueries: [{ query: GET_PETS }] .

Poiché il nostro obiettivo in questa sezione non è solo aggiornare i nostri animali domestici creati nell'interfaccia utente, ma manipolare la cache, non utilizzeremo questo metodo.

Il secondo approccio consiste nell'utilizzare la funzione di update . In Apollo Client, c'è una funzione di update che aiuta a modificare i dati della cache, in modo che si sincronizzi con le modifiche apportate da una mutazione ai nostri dati di back-end. Usando questa funzione, possiamo leggere e scrivere nella cache.

Aggiornamento della cache

Copia il seguente codice evidenziato e incollalo in client/src/pages/Pets.js :

 ...... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } );
 .....

La funzione di update riceve due argomenti:

  • Il primo argomento è la cache di Apollo Client.
  • Il secondo è l'esatta risposta di mutazione dal server. Destrutturiamo la proprietà dei data e la impostiamo sulla nostra mutazione ( addPet ).

Successivamente, per aggiornare la funzione, dobbiamo verificare quale query deve essere aggiornata (nel nostro caso, la query GET_PETS ) e leggere la cache.

In secondo luogo, dobbiamo scrivere sulla query che è stata letta, in modo che sappia che stiamo per aggiornarla. Lo facciamo passando un oggetto che contiene una proprietà dell'oggetto query , con il valore impostato sulla nostra operazione di query ( GET_PETS ), e una proprietà data il cui valore è un oggetto pet e che ha un array della mutazione addPet e una copia del dati dell'animale domestico.

Se hai seguito attentamente questi passaggi, dovresti vedere i tuoi animali domestici aggiornarsi automaticamente mentre li crei. Diamo un'occhiata alle modifiche:

Gli animali si aggiornano all'istante
Gli animali si aggiornano all'istante.

Interfaccia utente ottimista

Molte persone sono grandi fan di caricatori e spinner. Non c'è niente di sbagliato nell'usare un caricatore; ci sono casi d'uso perfetti in cui un caricatore è l'opzione migliore. Ho scritto di caricatori contro spinner e dei loro migliori casi d'uso.

Caricatori e spinner svolgono effettivamente un ruolo importante nella progettazione dell'interfaccia utente e dell'esperienza utente, ma l'arrivo dell'interfaccia utente ottimistica ha rubato i riflettori.

Che cos'è l'interfaccia utente ottimista?

Optimistic UI è una convenzione che simula i risultati di una mutazione (dati creati) e aggiorna l'interfaccia utente prima di ricevere una risposta dal server. Una volta ricevuta la risposta dal server, il risultato ottimistico viene buttato via e sostituito con il risultato effettivo.

Alla fine, un'interfaccia utente ottimista non è altro che un modo per gestire le prestazioni percepite ed evitare stati di caricamento.

Apollo Client ha un modo molto interessante di integrare l'interfaccia utente ottimistica. Ci fornisce un semplice hook che ci consente di scrivere nella cache locale dopo la mutazione. Vediamo come funziona!

Passo 1

Vai su client/src/client.js e aggiungi solo il codice evidenziato.

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context' import { ApolloLink } from 'apollo-link' const http = new HttpLink({ uri: "https://localhost:4000/" }); const delay = setContext( request => new Promise((success, fail) => { setTimeout(() => { success() }, 800) }) ) const link = ApolloLink.from([ delay, http ])
const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

Il primo passaggio prevede quanto segue:

  • Importiamo setContext da apollo-link-context . La funzione setContext accetta una funzione di callback e restituisce una promessa il cui setTimeout è impostato su 800ms , per creare un ritardo quando viene eseguita un'operazione di mutazione.
  • Il metodo ApolloLink.from garantisce che l'attività di rete che rappresenta il collegamento (la nostra API) da HTTP venga ritardata.

Passo 2

Il passaggio successivo consiste nell'usare l'hook Optimistic UI. Torna a client/src/pages/Pets.js e aggiungi solo il codice evidenziato di seguito.

 ..... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS); const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } ); const onSubmit = input => { setModal(false) createPet({ variables: { newPet: input },
 optimisticResponse: { __typename: 'Mutation', addPet: { __typename: 'Pet', id: Math.floor(Math.random() * 10000 + ''), name: input.name, type: input.type, img: 'https://via.placeholder.com/200' } }
 }); } .....

L'oggetto optimisticResponse viene utilizzato se desideriamo che l'interfaccia utente si aggiorni immediatamente quando creiamo un animale domestico, invece di attendere la risposta del server.

I frammenti di codice sopra includono quanto segue:

  • __typename viene inserito da Apollo nella query per recuperare il type delle entità richieste. Questi tipi vengono utilizzati da Apollo Client per creare la proprietà id (che è un simbolo) a scopo di memorizzazione nella cache in apollo-cache . Quindi, __typename è una proprietà valida della risposta alla query.
  • La mutazione è impostata come __typename di optimisticResponse .
  • Proprio come definito in precedenza, il nome della nostra mutazione è addPet e __typename è Pet .
  • I prossimi sono i campi della nostra mutazione che vogliamo che la risposta ottimistica aggiorni:
    • id
      Poiché non sappiamo quale sarà l'ID del server, ne abbiamo creato uno utilizzando Math.floor .
    • name
      Questo valore è impostato su input.name .
    • type
      Il valore del tipo è input.type .
    • img
      Ora, poiché il nostro server genera immagini per noi, abbiamo utilizzato un segnaposto per imitare la nostra immagine dal server.

Questo è stato davvero un lungo viaggio. Se sei arrivato alla fine, non esitare a fare una pausa dalla sedia con la tua tazza di caffè.

Diamo un'occhiata al nostro risultato. Il repository di supporto per questo progetto è su GitHub. Clona e sperimentalo.

Risultato finale dell'app negozio di animali
Risultato finale della nostra app.

Conclusione

Le straordinarie funzionalità di Apollo Client, come l'interfaccia utente ottimistica e l'impaginazione, rendono la creazione di app lato client una realtà.

Mentre Apollo Client funziona molto bene con altri framework, come Vue.js e Angular, gli sviluppatori React hanno Apollo Client Hooks e quindi non possono fare a meno di divertirsi a creare un'ottima app.

In questo articolo, abbiamo solo graffiato la superficie. Padroneggiare Apollo Client richiede pratica costante. Quindi, vai avanti e clona il repository, aggiungi l'impaginazione e gioca con le altre funzionalità che offre.

Per favore condividi il tuo feedback ed esperienza nella sezione commenti qui sotto. Possiamo anche discutere dei tuoi progressi su Twitter. Saluti!

Riferimenti

  • "Client-Side GraphQL In React", Scott Moss, Frontend Master
  • “Documentazione”, Cliente Apollo
  • "L'interfaccia utente ottimista con React", Patryk Andrzejewski
  • "Le vere bugie delle interfacce utente ottimistiche", Smashing Magazine