Înțelegerea GraphQl la nivelul clientului cu Apollo-Client în aplicațiile React

Publicat: 2022-03-10
Rezumat rapid ↬ Ați încercat vreodată să interacționați cu un server GraphQL într-o aplicație pe partea clientului și ați simțit că renunțați chiar înainte de a ajunge undeva? Ați refuzat vreodată o invitație de a vă alătura unei baze de cod care necesită lucrul cu API-ul GraphQL, deoarece habar n-aveai? V-ați simțit vreodată ca singurul inginer front-end care nu a învățat cum să consume API-urile GraphQL? Dacă ai răspuns da la oricare dintre aceste întrebări, atunci acest tutorial este pentru tine. Vom arunca o privire mai atentă la câteva elemente de bază ale GraphQL și Apollo Client, precum și cum să lucrăm cu ambele. Până la sfârșit, vom fi construit o aplicație pentru magazinul de animale de companie care utilizează Apollo Client. Apoi, puteți continua să construiți următorul proiect.

Conform State of JavaScript 2019, 38,7% dintre dezvoltatori ar dori să folosească GraphQL, în timp ce 50,8% dintre dezvoltatori ar dori să învețe GraphQL.

Fiind un limbaj de interogare, GraphQL simplifică fluxul de lucru al construirii unei aplicații client. Îndepărtează complexitatea gestionării punctelor finale API în aplicațiile client, deoarece expune un singur punct final HTTP pentru a prelua datele necesare. Prin urmare, elimină supraîncărcarea și preluarea insuficientă a datelor, ca în cazul REST.

Dar GraphQL este doar un limbaj de interogare. Pentru a-l folosi cu ușurință, avem nevoie de o platformă care să facă greutățile pentru noi. O astfel de platformă este Apollo.

Platforma Apollo este o implementare a GraphQL care transferă date între cloud (server) către interfața de utilizare a aplicației dvs. Când utilizați Apollo Client, toată logica pentru preluarea datelor, urmărirea, încărcarea și actualizarea interfeței de utilizare este încapsulată de cârligul useQuery (ca și în cazul React). Prin urmare, preluarea datelor este declarativă. Are, de asemenea, memoria cache cu configurație zero. Doar prin configurarea clientului Apollo în aplicația dvs., obțineți o memorie cache inteligentă din cutie, fără a fi necesară o configurare suplimentară.

Apollo Client este, de asemenea, interoperabil cu alte cadre, cum ar fi Angular, Vue.js și React.

Notă : Acest tutorial va beneficia de cei care au lucrat cu RESTful sau alte forme de API-uri în trecut pe partea clientului și vor să vadă dacă GraphQL merită să încerce. Aceasta înseamnă că ar fi trebuit să fi lucrat cu un API înainte; numai atunci vei putea înțelege cât de benefic ar putea fi GraphQL pentru tine. Deși vom acoperi câteva elemente de bază ale GraphQL și Apollo Client, o bună cunoaștere a JavaScript și React Hooks va fi utilă.

Bazele GraphQL

Acest articol nu este o introducere completă a GraphQL, dar vom defini câteva convenții înainte de a continua.

Ce este GraphQL?

GraphQL este o specificație care descrie un limbaj de interogare declarativ pe care clienții dvs. îl pot folosi pentru a solicita unui API datele exacte pe care le doresc. Acest lucru se realizează prin crearea unei scheme de tip puternice pentru API-ul dvs., cu flexibilitate maximă. De asemenea, asigură că API-ul rezolvă datele și că interogările clientului sunt validate în raport cu o schemă. Această definiție înseamnă că GraphQL conține unele specificații care îl fac un limbaj de interogare declarativ, cu un API care este tipat static (construit în jurul Typescript) și care face posibil ca clientul să folosească acele sisteme de tip pentru a cere API-ului datele exacte pe care le dorește .

Deci, dacă am creat unele tipuri cu unele câmpuri în ele, atunci, din partea clientului, am putea spune: „Dă-ne aceste date cu aceste câmpuri exacte”. Apoi API-ul va răspunde cu acea formă exactă, la fel ca și cum am folosi un sistem de tipări într-un limbaj puternic tastat. Puteți afla mai multe în articolul meu Typescript.

Să ne uităm la câteva convenții ale GraphQl care ne vor ajuta pe măsură ce continuăm.

Cele elementare

  • Operațiuni
    În GraphQL, fiecare acțiune efectuată se numește operație. Există câteva operații și anume:
    • Interogare
      Această operațiune se referă la preluarea datelor de pe server. L-ați putea numi și o preluare numai pentru citire.
    • Mutaţie
      Această operațiune implică crearea, actualizarea și ștergerea datelor de pe un server. Este denumită în mod popular o operațiune CUD (creare, actualizare și ștergere).
    • Abonamente
      Această operațiune în GraphQL implică trimiterea de date de la un server către clienții săi atunci când au loc anumite evenimente. Ele sunt de obicei implementate cu WebSockets.

În acest articol, ne vom ocupa doar de operații de interogare și mutație.

  • Numele operațiunilor
    Există nume unice pentru operațiunile dvs. de interogare și mutație la nivelul clientului.
  • Variabile și argumente
    Operațiile pot defini argumente, la fel ca o funcție în majoritatea limbajelor de programare. Aceste variabile pot fi apoi trecute la apeluri de interogare sau mutație în interiorul operației ca argumente. Se așteaptă ca variabilele să fie date în timpul execuției în timpul executării unei operațiuni de la clientul dumneavoastră.
  • Alianta
    Aceasta este o convenție în GraphQL pe partea client care implică redenumirea numelor de câmpuri detaliate sau vagi cu nume de câmp simple și lizibile pentru UI. Aliasarea este necesară în cazurile de utilizare în care nu doriți să aveți nume de câmpuri conflictuale.
Convenții de bază GraphQL
Convenții de bază GraphQL. (Previzualizare mare)
Mai multe după săritură! Continuați să citiți mai jos ↓

Ce este Client-Side GraphQL?

Când un inginer front-end construiește componente UI folosind orice cadru, cum ar fi Vue.js sau (în cazul nostru) React, acele componente sunt modelate și proiectate după un anumit model pe client pentru a se potrivi cu datele care vor fi preluate de pe server.

Una dintre cele mai frecvente probleme cu API-urile RESTful este supraîncărcarea și preluarea insuficientă. Acest lucru se întâmplă deoarece singura modalitate prin care un client poate descărca date este prin lovirea punctelor finale care returnează structuri de date fixe . Supracărcarea în acest context înseamnă că un client descarcă mai multe informații decât este cerut de aplicație.

În GraphQL, pe de altă parte, ați trimite pur și simplu o singură interogare la serverul GraphQL care include datele necesare. Serverul va răspunde apoi cu un obiect JSON cu datele exacte pe care le-ați solicitat - prin urmare, fără supraîncărcare. Sebastian Eschweiler explică diferențele dintre API-urile RESTful și GraphQL.

GraphQL pe partea clientului este o infrastructură pe partea clientului care interfață cu datele de pe un server GraphQL pentru a îndeplini următoarele funcții:

  • Gestionează datele prin trimiterea de interogări și mutarea datelor fără a fi nevoie să construiți cereri HTTP singur. Puteți petrece mai puțin timp cu datele de instalații și mai mult timp pentru a construi aplicația reală.
  • Gestionează complexitatea unui cache pentru tine. Așadar, puteți stoca și prelua datele preluate de pe server, fără nicio interferență de la terți și puteți evita cu ușurință recuperarea resurselor duplicate. Astfel, identifică când două resurse sunt aceleași, ceea ce este grozav pentru o aplicație complexă.
  • Vă păstrează interfața de utilizare în concordanță cu Optimistic UI, o convenție care simulează rezultatele unei mutații (adică datele create) și actualizează interfața de utilizare chiar înainte de a primi un răspuns de la server. Odată ce răspunsul este primit de la server, rezultatul optimist este aruncat și înlocuit cu rezultatul real.

Pentru mai multe informații despre GraphQL la nivelul clientului, petreceți o oră cu co-creatorul GraphQL și alți oameni cool de pe GraphQL Radio.

Ce este Apollo Client?

Apollo Client este un client GraphQL interoperabil, ultra-flexibil, condus de comunitate pentru JavaScript și platforme native. Caracteristicile sale impresionante includ un instrument robust de gestionare a stării (Apollo Link), un sistem de stocare în cache fără configurație, o abordare declarativă pentru preluarea datelor, paginare ușor de implementat și interfața de utilizare Optimistic pentru aplicația dvs. de la partea clientului.

Apollo Client stochează nu numai starea din datele preluate de pe server, ci și starea pe care a creat-o local pe clientul dvs.; prin urmare, gestionează starea atât pentru datele API, cât și pentru datele locale.

De asemenea, este important să rețineți că puteți utiliza Apollo Client alături de alte instrumente de management al statului, cum ar fi Redux, fără conflicte. În plus, este posibil să migrați managementul de stat de la, de exemplu, Redux la Apollo Client (ceea ce depășește domeniul de aplicare al acestui articol). În cele din urmă, scopul principal al clientului Apollo este de a permite inginerilor să interogheze datele într-un API fără probleme.

Caracteristicile clientului Apollo

Clientul Apollo a cucerit atât de mulți ingineri și companii datorită caracteristicilor sale extrem de utile, care fac ca construirea de aplicații moderne și robuste să fie o ușoară. Următoarele caracteristici sunt coapte:

  • Memorarea în cache
    Clientul Apollo acceptă stocarea în cache din mers.
  • Interfață de utilizare optimistă
    Clientul Apollo are un suport grozav pentru interfața de utilizare Optimistic. Implică afișarea temporară a stării finale a unei operații (mutație) în timp ce operația este în desfășurare. Odată ce operațiunea este finalizată, datele reale înlocuiesc datele optimiste.
  • Paginare
    Apollo Client are o funcționalitate încorporată care face destul de ușor să implementați paginarea în aplicația dvs. Se ocupă de majoritatea durerilor de cap tehnice ale preluării unei liste de date, fie în patch-uri, fie odată, folosind funcția fetchMore , care vine cu cârligul useQuery .

În acest articol, vom analiza o selecție a acestor caracteristici.

Ajunge cu teorie. Strângeți centura de siguranță și luați o ceașcă de cafea pentru a merge cu clătitele, în timp ce ne murdărim mâinile.

Construirea aplicației noastre web

Acest proiect este inspirat de Scott Moss.

Vom construi o aplicație web simplă pentru magazinul de animale de companie, ale cărei caracteristici includ:

  • ne aducem animalele de companie de pe partea serverului;
  • crearea unui animal de companie (care implică crearea numelui, tipului de animal de companie și a imaginii);
  • folosind interfața de utilizare optimistă;
  • folosind paginarea pentru a ne segmenta datele.

Pentru a începe, clonați depozitul, asigurându-vă că ramura de starter este ceea ce ați clonat.

Noțiuni de bază

  • Instalați extensia Apollo Client Developer Tools pentru Chrome.
  • Folosind interfața de linie de comandă (CLI), navigați la directorul depozitului clonat și rulați comanda pentru a obține toate dependențele: npm install .
  • Rulați comanda npm run app pentru a porni aplicația.
  • În timp ce încă vă aflați în folderul rădăcină, executați comanda npm run server . Acest lucru va porni serverul nostru back-end pentru noi, pe care îl vom folosi pe măsură ce continuăm.

Aplicația ar trebui să se deschidă într-un port configurat. Al meu este https://localhost:1234/ ; a ta este probabil altceva.

Dacă totul a funcționat bine, aplicația dvs. ar trebui să arate astfel:

Interfață de utilizare pentru ramură de pornire clonată
Interfață de utilizare pentru ramură de pornire clonată. (Previzualizare mare)

Veți observa că nu avem animale de companie de afișat. Asta pentru că nu am creat încă o astfel de funcționalitate.

Dacă ați instalat corect Apollo Client Developer Tools, deschideți instrumentele pentru dezvoltatori și faceți clic pe pictograma tavă. Veți vedea „Apollo” și ceva de genul acesta:

Instrumente pentru dezvoltatori client Apollo
Instrumente pentru dezvoltatori client Apollo. (Previzualizare mare)

La fel ca instrumentele pentru dezvoltatori Redux și React, vom folosi instrumentele pentru dezvoltatori client Apollo pentru a scrie și testa interogările și mutațiile noastre. Extensia vine cu GraphQL Playground.

Preluarea animalelor de companie

Să adăugăm funcționalitatea care preia animalele de companie. Treceți la client/src/client.js . Vom scrie Apollo Client, îl vom conecta la un API, îl vom exporta ca client implicit și vom scrie o nouă interogare.

Copiați următorul cod și inserați-l în 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

Iată o explicație a ceea ce se întâmplă mai sus:

  • ApolloClient
    Aceasta va fi funcția care include aplicația noastră și, astfel, interfață cu HTTP, memorează datele în cache și actualizează interfața de utilizare.
  • InMemoryCache
    Acesta este depozitul de date normalizat din Apollo Client care ajută la manipularea cache-ului în aplicația noastră.
  • HttpLink
    Aceasta este o interfață de rețea standard pentru modificarea fluxului de control al solicitărilor GraphQL și preluarea rezultatelor GraphQL. Acesta acționează ca middleware, preluând rezultate de la serverul GraphQL de fiecare dată când legătura este declanșată. În plus, este un înlocuitor bun pentru alte opțiuni, cum ar fi Axios și window.fetch .
  • Declarăm o variabilă de legătură care este atribuită unei instanțe de HttpLink . Este nevoie de o proprietate uri și o valoare pentru serverul nostru, care este https://localhost:4000/ .
  • Urmează o variabilă cache care deține noua instanță a InMemoryCache .
  • Variabila client preia, de asemenea, o instanță a lui ApolloClient și include link și cache .
  • În cele din urmă, exportăm client , astfel încât să-l putem folosi în cadrul aplicației.

Înainte de a ajunge să vedem acest lucru în acțiune, trebuie să ne asigurăm că întreaga noastră aplicație este expusă la Apollo și că aplicația noastră poate primi date preluate de la server și că poate modifica acele date.

Pentru a realiza acest lucru, să mergem la 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() }

După cum veți observa în codul evidențiat, am împachetat componenta App în ApolloProvider și i-am transmis clientului ca suport client . ApolloProvider este similar cu Context.Provider al lui React. Acesta include aplicația dvs. React și plasează clientul în context, ceea ce vă permite să îl accesați de oriunde în arborele dvs. de componente.

Pentru a ne prelua animalele de companie de pe server, trebuie să scriem interogări care solicită câmpurile exacte pe care le dorim. Mergeți la client/src/pages/Pets.js și copiați și inserați următorul cod în el:

 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> ) }

Cu câteva bucăți de cod, putem prelua animalele de companie de pe server.

Ce este gql?

Este important de reținut că operațiunile din GraphQL sunt, în general, obiecte JSON scrise cu graphql-tag și cu backtick-uri.

Etichetele gql sunt etichete literale de șablon JavaScript care analizează șirurile de interogări GraphQL în AST GraphQL (arborele de sintaxă abstractă).

  • Operații de interogare
    Pentru a ne prelua animalele de companie de pe server, trebuie să efectuăm o operațiune de interogare.
    • Deoarece efectuăm o operațiune de query , trebuia să specificăm type de operație înainte de a o numi.
    • Numele interogării noastre este GET_PETS . Este o convenție de denumire a GraphQL să folosești camelCase pentru numele câmpurilor.
    • Numele câmpurilor noastre este pets de companie. Prin urmare, specificăm câmpurile exacte de care avem nevoie de la server (id, name, type, img) .
    • useQuery este un cârlig React care stă la baza executării interogărilor într-o aplicație Apollo. Pentru a efectua o operație de interogare în componenta noastră React, numim cârligul useQuery , care a fost importat inițial din @apollo/react-hooks . Apoi, îi transmitem un șir de interogare GraphQL, care este GET_PETS în cazul nostru.
  • Când componenta noastră este redată, useQuery returnează un răspuns de la Apollo Client care conține proprietăți de încărcare, eroare și date. Astfel, ele sunt destructurate, astfel încât să le putem folosi pentru a reda UI.
  • useQuery este minunat. Nu trebuie să includem async-await . Este deja îngrijită în fundal. Destul de tare, nu-i așa?
    • loading
      Această proprietate ne ajută să gestionăm starea de încărcare a aplicației. În cazul nostru, returnăm o componentă Loader în timp ce aplicația noastră se încarcă. În mod implicit, încărcarea este false .
    • error
      Pentru orice eventualitate, folosim această proprietate pentru a gestiona orice eroare care ar putea apărea.
    • data
      Acesta conține datele noastre reale de pe server.
    • În sfârșit, în componenta noastră PetsList , trecem elementele de recuzită pentru pets de companie, cu data.pets ca valoare a obiectului.

În acest moment, am interogat cu succes serverul nostru.

Pentru a porni aplicația noastră, să rulăm următoarea comandă:

  • Porniți aplicația client. Rulați comanda npm run app în CLI.
  • Porniți serverul. Rulați comanda npm run server într-un alt CLI.
VScode CLI partiționat pentru a porni atât clientul, cât și serverul.
VScode CLI partiționat pentru a porni atât clientul, cât și serverul. (Previzualizare mare)

Dacă totul a mers bine, ar trebui să vezi asta:

Animale de companie interogate de pe server.
Animale de companie interogate de pe server.

Mutarea datelor

Mutarea datelor sau crearea datelor în Apollo Client este aproape la fel cu interogarea datelor, cu modificări foarte mici.

Încă în client/src/pages/Pets.js , să copiem și să lipim codul evidențiat:

 .... 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

Pentru a crea o mutație, vom face următorii pași.

1. mutation

Pentru a crea, actualiza sau șterge, trebuie să efectuăm operația de mutation . Operația de mutation are un nume CreateAPet , cu un singur argument. Acest argument are o variabilă $newPet , cu un tip de NewPetInput . Cel ! înseamnă că operațiunea este necesară; astfel, GraphQL nu va executa operația decât dacă trecem o variabilă newPet al cărei tip este NewPetInput .

2. addPet

Funcția addPet , care se află în interiorul operației de mutation , ia un argument de input și este setată la variabila noastră $newPet . Seturile de câmpuri specificate în funcția noastră addPet trebuie să fie egale cu seturile de câmpuri din interogarea noastră. Seturile de câmp din operațiunea noastră sunt:

  • id
  • name
  • type
  • img

3. useMutation

useMutation React este API-ul principal pentru executarea mutațiilor într-o aplicație Apollo. Când trebuie să modificăm datele, numim useMutation într-o componentă React și îi transmitem un șir GraphQL (în cazul nostru, NEW_PETS ).

Când componenta noastră redă useMutation , returnează un tuplu (adică un set ordonat de date care constituie o înregistrare) într-o matrice care include:

  • o funcție de mutate pe care o putem apela în orice moment pentru a executa mutația;
  • un obiect cu câmpuri care reprezintă starea curentă a execuției mutației.

Cârligul useMutation primește un șir de mutație GraphQL (care este NEW_PETS în cazul nostru). Am destructurat tuplu, care este funcția ( createPet ) care va muta datele și câmpul obiect ( newPets ).

4. createPet

În funcția noastră onSubmit , la scurt timp după starea setModal , am definit createPet -ul nostru. Această funcție preia o variable cu o proprietate obiect a unei valori setată la { newPet: input } . input reprezintă diferitele câmpuri de intrare din formularul nostru (cum ar fi numele, tipul etc.).

După aceasta, rezultatul ar trebui să arate astfel:

Mutație fără actualizare instantanee
Mutație fără actualizare instantanee.

Dacă observați îndeaproape GIF-ul, veți observa că animalul nostru de companie creat nu apare instantaneu, doar când pagina este reîmprospătată. Cu toate acestea, a fost actualizat pe server.

Marea întrebare este, de ce animalul nostru de companie nu se actualizează instantaneu? Să aflăm în secțiunea următoare.

Memorarea în cache în clientul Apollo

Motivul pentru care aplicația noastră nu se actualizează automat este că datele noastre nou create nu se potrivesc cu datele cache din Apollo Client. Deci, există un conflict cu privire la ce anume trebuie să fie actualizat din cache.

Mai simplu spus, dacă efectuăm o mutație care actualizează sau șterge mai multe intrări (un nod), atunci suntem responsabili pentru actualizarea oricăror interogări care fac referire la acel nod, astfel încât să modifice datele noastre din cache pentru a se potrivi cu modificările pe care o mutație le face în spatele nostru. date finale .

Păstrarea memoriei cache în sincronizare

Există câteva modalități de a menține memoria cache sincronizată de fiecare dată când efectuăm o operație de mutare.

Primul este prin preluarea interogărilor care se potrivesc după o mutație, folosind proprietatea obiectului refetchQueries (cel mai simplu mod).

Notă: Dacă ar fi să folosim această metodă, ar fi nevoie de o proprietate de obiect în funcția noastră createPet numită refetchQueries și ar conține o matrice de obiecte cu o valoare a interogării: refetchQueries: [{ query: GET_PETS }] .

Deoarece concentrarea noastră în această secțiune nu este doar să actualizăm animalele de companie create în interfața de utilizare, ci și să manipulăm memoria cache, nu vom folosi această metodă.

A doua abordare este utilizarea funcției de update . În Apollo Client, există o funcție de ajutor de update care ajută la modificarea datelor din cache, astfel încât să se sincronizeze cu modificările pe care o mutație le aduce datelor noastre back-end. Folosind această funcție, putem citi și scrie în cache.

Actualizarea cache-ului

Copiați următorul cod evidențiat și inserați-l în 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] }, }); }, } );
 .....

Funcția de update primește două argumente:

  • Primul argument este memoria cache de la Apollo Client.
  • Al doilea este răspunsul exact la mutație de la server. Destructuram proprietatea data și o setăm la mutația noastră ( addPet ).

Apoi, pentru a actualiza funcția, trebuie să verificăm ce interogare trebuie actualizată (în cazul nostru, interogarea GET_PETS ) și să citim memoria cache.

În al doilea rând, trebuie să scriem la query care a fost citită, astfel încât să știe că suntem pe cale să o actualizăm. Facem acest lucru prin transmiterea unui obiect care conține o proprietate de obiect de query , cu valoarea setată la operația noastră de query ( GET_PETS ) și o proprietate de data a cărei valoare este un obiect de pet și care are o matrice a mutației addPet și o copie a datele animalelor de companie.

Dacă ați urmat acești pași cu atenție, ar trebui să vedeți că animalele dvs. de companie se actualizează automat pe măsură ce le creați. Să aruncăm o privire asupra modificărilor:

Animalele de companie se actualizează instantaneu
Animalele de companie se actualizează instantaneu.

Interfață de utilizare optimistă

Mulți oameni sunt mari fani ai încărcătoarelor și spinner-urilor. Nu este nimic în neregulă cu utilizarea unui încărcător; există cazuri de utilizare perfecte în care încărcătorul este cea mai bună opțiune. Am scris despre încărcătoare versus filatoare și cele mai bune cazuri de utilizare ale acestora.

Încărcătoarele și spinnerele joacă într-adevăr un rol important în designul UI și UX, dar apariția Optimistic UI a furat lumina reflectoarelor.

Ce este Optimistic UI?

Optimistic UI este o convenție care simulează rezultatele unei mutații (date create) și actualizează UI înainte de a primi un răspuns de la server. Odată ce răspunsul este primit de la server, rezultatul optimist este aruncat și înlocuit cu rezultatul real.

În cele din urmă, o interfață de utilizare optimistă nu este altceva decât o modalitate de a gestiona performanța percepută și de a evita stările de încărcare.

Apollo Client are o modalitate foarte interesantă de a integra interfața de utilizare Optimistic. Ne oferă un cârlig simplu care ne permite să scriem în memoria cache locală după mutație. Să vedem cum funcționează!

Pasul 1

Mergeți la client/src/client.js și adăugați numai codul evidențiat.

 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

Primul pas implică următoarele:

  • Importăm setContext din apollo-link-context . Funcția setContext preia o funcție de apel invers și returnează o promisiune a cărei setTimeout este setat la 800ms , pentru a crea o întârziere atunci când este efectuată o operație de mutație.
  • Metoda ApolloLink.from asigură că activitatea de rețea care reprezintă legătura (API-ul nostru) de la HTTP este întârziată.

Pasul 2

Următorul pas este utilizarea cârligului Optimistic UI. Glisați înapoi la client/src/pages/Pets.js și adăugați doar codul evidențiat mai jos.

 ..... 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' } }
 }); } .....

Obiectul optimisticResponse este folosit dacă dorim ca UI să se actualizeze imediat când creăm un animal de companie, în loc să așteptăm răspunsul serverului.

Fragmentele de cod de mai sus includ următoarele:

  • __typename este injectat de Apollo în interogare pentru a prelua type entităților interogate. Aceste tipuri sunt folosite de Apollo Client pentru a construi proprietatea id (care este un simbol) în scopuri de stocare în cache în apollo-cache . Deci, __typename este o proprietate validă a răspunsului la interogare.
  • Mutația este setată ca __typename al optimisticResponse .
  • Așa cum sa definit mai devreme, numele mutației noastre este addPet , iar __typename este Pet .
  • Urmează câmpurile mutației noastre pe care dorim să le actualizeze răspunsul optimist:
    • id
      Pentru că nu știm care va fi ID-ul de pe server, am creat unul folosind Math.floor .
    • name
      Această valoare este setată la input.name .
    • type
      Valoarea tipului este input.type .
    • img
      Acum, deoarece serverul nostru generează imagini pentru noi, am folosit un substituent pentru a imita imaginea noastră de pe server.

Aceasta a fost într-adevăr o călătorie lungă. Dacă ai ajuns până la capăt, nu ezita să iei o pauză de pe scaun cu ceașca ta de cafea.

Să aruncăm o privire la rezultatul nostru. Depozitul de sprijin pentru acest proiect este pe GitHub. Clonează și experimentează cu el.

Rezultatul final al aplicației magazin de animale de companie
Rezultatul final al aplicației noastre.

Concluzie

Caracteristicile uimitoare ale Clientului Apollo, cum ar fi interfața de utilizare optimistă și paginarea, fac din construirea aplicațiilor client o realitate.

În timp ce Apollo Client funcționează foarte bine cu alte cadre, cum ar fi Vue.js și Angular, dezvoltatorii React au Apollo Client Hooks și, prin urmare, nu se pot abține să nu se bucure de construirea unei aplicații grozave.

În acest articol, am zgâriat doar suprafața. Stăpânirea clientului Apollo necesită practică constantă. Deci, mergeți mai departe și clonați depozitul, adăugați paginare și jucați-vă cu celelalte funcții pe care le oferă.

Vă rugăm să împărtășiți feedback-ul și experiența dvs. în secțiunea de comentarii de mai jos. Putem discuta despre progresul tău și pe Twitter. Noroc!

Referințe

  • „Client-Side GraphQL In React”, Scott Moss, Master Frontend
  • „Documentare”, Client Apollo
  • „Interfața de utilizare optimistă cu React”, Patryk Andrzejewski
  • „Adevărate minciuni ale interfețelor de utilizator optimiste”, Smashing Magazine