Înțelegerea GraphQl la nivelul clientului cu Apollo-Client în aplicațiile React
Publicat: 2022-03-10Conform 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.
- Interogare
Î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.
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țiafetchMore
, care vine cu cârliguluseQuery
.
Î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:
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:
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 fiAxios
șiwindow.fetch
. - Declarăm o variabilă de legătură care este atribuită unei instanțe de
HttpLink
. Este nevoie de o proprietateuri
și o valoare pentru serverul nostru, care estehttps://localhost:4000/
. - Urmează o variabilă cache care deține noua instanță a
InMemoryCache
. - Variabila client preia, de asemenea, o instanță a lui
ApolloClient
și includelink
șicache
. - Î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ămtype
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ârliguluseQuery
, care a fost importat inițial din@apollo/react-hooks
. Apoi, îi transmitem un șir de interogare GraphQL, care esteGET_PETS
în cazul nostru.
- Deoarece efectuăm o operațiune de
- 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ă includemasync-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 estefalse
. -
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ă pentrupets
de companie, cudata.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.
Dacă totul a mers bine, ar trebui să vezi asta:
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:
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:
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
dinapollo-link-context
. FuncțiasetContext
preia o funcție de apel invers și returnează o promisiune a căreisetTimeout
este setat la800ms
, 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 laHTTP
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 preluatype
entităților interogate. Aceste tipuri sunt folosite de Apollo Client pentru a construi proprietateaid
(care este un simbol) în scopuri de stocare în cache înapollo-cache
. Deci,__typename
este o proprietate validă a răspunsului la interogare. - Mutația este setată ca
__typename
aloptimisticResponse
. - Așa cum sa definit mai devreme, numele mutației noastre este
addPet
, iar__typename
estePet
. - 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 folosindMath.floor
. -
name
Această valoare este setată lainput.name
. -
type
Valoarea tipului esteinput.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.
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