Preluarea datelor învechite în timp ce se revalidează cu React Hooks: un ghid
Publicat: 2022-03-11Utilizarea extensiei HTTP Cache-Control
învechite în timp ce se revalidează este o tehnică populară. Aceasta implică utilizarea activelor stocate în cache (învechite) dacă acestea se găsesc în cache, apoi revalidarea memoriei cache și actualizarea acestuia cu o versiune mai nouă a activului, dacă este necesar. De aici și numele stale-while-revalidate
.
Cum funcționează stale-while-revalidate
Când o solicitare este trimisă pentru prima dată, aceasta este stocată în cache de browser. Apoi, când aceeași cerere este trimisă a doua oară, mai întâi se verifică memoria cache. Dacă memoria cache a cererii respective este disponibilă și validă, memoria cache este returnată ca răspuns. Apoi, memoria cache este verificată pentru învechire și este actualizată dacă este învechită. Staleness-ul unui cache este determinat de valoarea max-age
prezentă în antetul Cache-Control
împreună cu stale-while-revalidate
.
Acest lucru permite încărcarea rapidă a paginii, deoarece activele stocate în cache nu se mai află pe calea critică. Sunt încărcate instantaneu. De asemenea, deoarece dezvoltatorii controlează cât de des este utilizat și actualizat memoria cache, ei pot împiedica browserele să arate utilizatorilor date prea învechite.
Cititorii s-ar putea gândi că, dacă pot avea serverul să folosească anumite antete în răspunsurile sale și să lase browserul să le ia de acolo, atunci de ce este nevoie să folosești React și Hooks pentru stocarea în cache?
Se pare că abordarea server-și-browser funcționează bine numai atunci când dorim să memorăm în cache conținut static. Ce zici de utilizarea stale-while-revalidate
pentru un API dinamic? Este greu să veniți cu valori bune pentru max-age
și stale-while-revalidate
în acest caz. Adesea, invalidarea memoriei cache și obținerea unui răspuns nou de fiecare dată când este trimisă o solicitare va fi cea mai bună opțiune. Acest lucru înseamnă în mod efectiv lipsa de memorare în cache. Dar cu React și Hooks, putem face mai bine.
stale-while-revalidate
pentru API
Am observat că stale-while-revalidate
nu funcționează bine cu solicitările dinamice precum apelurile API.
Chiar dacă ajungem să-l folosim, browserul va returna fie memoria cache, fie răspunsul proaspăt, nu ambele. Acest lucru nu merge bine cu o solicitare API, deoarece am dori răspunsuri noi de fiecare dată când este trimisă o solicitare. Cu toate acestea, așteptarea unor răspunsuri noi întârzie utilizarea semnificativă a aplicației.
Deci ce facem?
Implementăm un mecanism personalizat de stocare în cache. În acest sens, găsim o modalitate de a returna atât memoria cache, cât și răspunsul proaspăt. În interfața de utilizare, răspunsul stocat în cache este înlocuit cu un răspuns nou atunci când este disponibil. Cam asa ar arata logica:
- Când o solicitare este trimisă la punctul final al serverului API pentru prima dată, memorați răspunsul în cache și apoi returnați-l.
- Data viitoare când are loc aceeași solicitare API, utilizați imediat răspunsul din cache.
- Apoi, trimiteți cererea în mod asincron pentru a obține un nou răspuns. Când sosește răspunsul, propagați asincron modificările în interfața de utilizare și actualizați memoria cache.
Această abordare permite actualizări instantanee ale interfeței de utilizare – pentru că fiecare solicitare API este stocată în cache – dar și eventuala corectitudine în interfața de utilizare, deoarece datele de răspuns proaspete sunt afișate de îndată ce sunt disponibile.
În acest tutorial, vom vedea o abordare pas cu pas despre cum să implementăm acest lucru. Vom numi această abordare învechită în timp ce reîmprospătare , deoarece interfața de utilizare este de fapt reîmprospătată atunci când primește un răspuns proaspăt.
Pregătiri: API
Pentru a lansa acest tutorial, vom avea nevoie mai întâi de un API de unde obținem date. Din fericire, există o mulțime de servicii API simulate disponibile. Pentru acest tutorial, vom folosi reqres.in.
Datele pe care le preluăm sunt o listă de utilizatori cu un parametru de interogare a page
. Iată cum arată codul de preluare:
fetch("https://reqres.in/api/users?page=2") .then(res => res.json()) .then(json => { console.log(json); });
Rularea acestui cod ne oferă următoarea ieșire. Iată o versiune nerepetitivă a acesteia:
{ page: 2, per_page: 6, total: 12, total_pages: 2, data: [ { id: 7, email: "[email protected]", first_name: "Michael", last_name: "Lawson", avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg" }, // 5 more items ] }
Puteți vedea că acesta este ca un API adevărat. Avem paginație în răspuns. Parametrul de interogare a page
este responsabil pentru schimbarea paginii și avem în total două pagini în setul de date.
Utilizarea API-ului într-o aplicație React
Să vedem cum folosim API-ul într-o aplicație React. Odată ce știm cum să o facem, ne vom da seama de partea de cache. Vom folosi o clasă pentru a crea componenta noastră. Iată codul:
import React from "react"; import PropTypes from "prop-types"; export default class Component extends React.Component { state = { users: [] }; componentDidMount() { this.load(); } load() { fetch(`https://reqres.in/api/users?page=${this.props.page}`) .then(res => res.json()) .then(json => { this.setState({ users: json.data }); }); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { const users = this.state.users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{users}</div>; } } Component.propTypes = { page: PropTypes.number.isRequired };
Observați că obținem valoarea page
prin props
, așa cum se întâmplă adesea în aplicațiile din lumea reală. De asemenea, avem o funcție componentDidUpdate
, care recuperează datele API de fiecare dată când this.props.page
se modifică.
În acest moment, arată o listă de șase utilizatori, deoarece API-ul returnează șase articole pe pagină:
Adăugarea de cache învechită în timp ce se reîmprospătează
Dacă dorim să adăugăm cache învechită în timp ce reîmprospătăm, trebuie să ne actualizăm logica aplicației la:
- Memorați în cache răspunsul unei cereri în mod unic după ce este preluat pentru prima dată.
- Întoarceți instantaneu răspunsul stocat în cache dacă este găsită memoria cache a unei cereri. Apoi, trimiteți cererea și returnați răspunsul proaspăt în mod asincron. De asemenea, memorați în cache acest răspuns pentru data viitoare.
Putem face acest lucru având un obiect CACHE
global care stochează cache-ul în mod unic. Pentru unicitate, putem folosi valoarea this.props.page
ca cheie în obiectul nostru CACHE
. Apoi, codificăm pur și simplu algoritmul menționat mai sus.
import apiFetch from "./apiFetch"; const CACHE = {}; export default class Component extends React.Component { state = { users: [] }; componentDidMount() { this.load(); } load() { if (CACHE[this.props.page] !== undefined) { this.setState({ users: CACHE[this.props.page] }); } apiFetch(`https://reqres.in/api/users?page=${this.props.page}`).then( json => { CACHE[this.props.page] = json.data; this.setState({ users: json.data }); } ); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { // same render code as above } }
Deoarece memoria cache este returnată de îndată ce este găsită și din moment ce noile date de răspuns sunt returnate și de setState
, aceasta înseamnă că avem actualizări fără probleme ale UI și nu mai avem timp de așteptare în aplicație de la a doua solicitare încolo. Aceasta este perfectă și, pe scurt, este metoda învechită în timp ce se reîmprospătează.
Funcția apiFetch
de aici nu este altceva decât un wrapper peste fetch
, astfel încât să putem vedea avantajul stocării în cache în timp real. Face acest lucru prin adăugarea unui utilizator aleatoriu la lista de users
returnați de solicitarea API. De asemenea, îi adaugă o întârziere aleatorie:
export default async function apiFetch(...args) { await delay(Math.ceil(400 + Math.random() * 300)); const res = await fetch(...args); const json = await res.json(); json.data.push(getFakeUser()); return json; } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
Funcția getFakeUser()
de aici este responsabilă pentru crearea unui obiect utilizator fals.
Cu aceste modificări, API-ul nostru este mai real decât înainte.
- Are o întârziere aleatorie în răspuns.
- Returnează date ușor diferite pentru aceleași solicitări.
Având în vedere acest lucru, atunci când schimbăm prop page
transmisă Component
din componenta noastră principală, putem vedea cache-ul API-ului în acțiune. Încercați să faceți clic pe butonul Comutare o dată la câteva secunde în acest CodeSandbox și ar trebui să vedeți un comportament ca acesta:
Dacă te uiți cu atenție, se întâmplă câteva lucruri.
- Când aplicația pornește și este în starea implicită, vedem o listă de șapte utilizatori. Luați notă de ultimul utilizator de pe listă, deoarece acesta este utilizatorul care va fi modificat aleatoriu data viitoare când această solicitare va fi trimisă.
- Când facem clic pe Comutare pentru prima dată, acesta așteaptă o perioadă mică de timp (400-700 ms) și apoi actualizează lista la pagina următoare.
- Acum suntem pe a doua pagină. Luați din nou notă de ultimul utilizator din listă.
- Acum, facem clic din nou pe Comutare, iar aplicația va reveni la prima pagină. Observați că acum ultima intrare este în continuare același utilizator pe care l-am notat la Pasul 1, iar apoi se schimbă ulterior în noul utilizator (aleatoriu). Acest lucru se datorează faptului că, inițial, memoria cache a fost afișată, iar apoi răspunsul real a intervenit.
- Facem din nou clic pe Comutare. Același fenomen se întâmplă. Răspunsul stocat în cache de la ultima dată este încărcat instantaneu, apoi sunt preluate date noi și astfel vedem ultima actualizare a intrării din ceea ce am notat la Pasul 3.
Acesta este, memoria cache învechită în timp ce-reîmprospătam pe care o căutam. Dar această abordare suferă de o problemă de duplicare a codului. Să vedem cum merge dacă avem o altă componentă de preluare a datelor cu cache. Această componentă arată articolele diferit de prima noastră componentă.
Adăugarea Stale-while-refresh la o altă componentă
Putem face acest lucru prin simpla copiere a logicii din prima componentă. A doua componentă a noastră arată o listă de pisici:
const CACHE = {}; export default class Component2 extends React.Component { state = { cats: [] }; componentDidMount() { this.load(); } load() { if (CACHE[this.props.page] !== undefined) { this.setState({ cats: CACHE[this.props.page] }); } apiFetch(`https://reqres.in/api/cats?page=${this.props.page}`).then( json => { CACHE[this.props.page] = json.data; this.setState({ cats: json.data }); } ); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { const cats = this.state.cats.map(cat => ( <p key={cat.id} style={{ background: cat.color, padding: "4px", width: 240 }} > {cat.name} (born {cat.year}) </p> )); return <div>{cats}</div>; } }
După cum puteți vedea, logica componentelor implicate aici este aproape aceeași cu prima componentă. Singura diferență este în punctul final solicitat și că arată elementele din listă în mod diferit.
Acum, arătăm ambele componente una lângă alta. Puteți vedea că se comportă similar:
Pentru a obține acest rezultat, a trebuit să facem multă duplicare a codului. Dacă am avea mai multe componente ca aceasta, am duplica prea mult cod.
Pentru a o rezolva într-o manieră care nu se dublează, putem avea o componentă de ordin superior pentru preluarea și stocarea în cache a datelor și transmiterea lor ca elemente de recuzită. Nu este ideal, dar va funcționa. Dar dacă ar trebui să facem mai multe solicitări într-o singură componentă, a avea mai multe componente de ordin superior ar deveni urât foarte repede.
Apoi, avem modelul de recuzită de randare, care este probabil cel mai bun mod de a face acest lucru în componentele clasei. Funcționează perfect, dar, din nou, este predispus la „iad de înveliș” și ne cere să legăm uneori contextul actual. Aceasta nu este o experiență excelentă pentru dezvoltatori și poate duce la frustrare și erori.
Aici React Hooks salvează ziua. Ele ne permit să ambalăm logica componentelor într-un container reutilizabil, astfel încât să o putem folosi în mai multe locuri. React Hooks au fost introduse în React 16.8 și funcționează numai cu componente de funcție. Înainte de a ajunge la React cache control, în special, stocarea în cache a conținutului cu Hooks, să vedem mai întâi cum facem preluarea simplă a datelor în componentele funcției.

Preluarea datelor API în componentele funcției
Pentru a prelua date API din componentele funcției, folosim cârligele useState
și useEffect
.
useState
este analog cu state
componentelor clasei și setState
. Folosim acest cârlig pentru a avea containere atomice de stare în interiorul unei componente de funcție.
useEffect
este un cârlig pentru ciclul de viață și vă puteți gândi la el ca o combinație de componentDidMount
, componentDidUpdate
și componentWillUnmount
. Al doilea parametru transmis la useEffect
se numește matrice de dependențe. Când matricea de dependență se modifică, apelul invers transmis ca prim argument pentru useEffect
este rulat din nou.
Iată cum vom folosi aceste cârlige pentru a implementa preluarea datelor:
import React, { useState, useEffect } from "react"; export default function Component({ page }) { const [users, setUsers] = useState([]); useEffect(() => { fetch(`https://reqres.in/api/users?page=${page}`) .then(res => res.json()) .then(json => { setUsers(json.data); }); }, [page]); const usersDOM = users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }
Specificând page
ca dependență de useEffect
, îi instruim pe React să ruleze apelul nostru useEffect de fiecare dată când page
este schimbată. Acesta este la fel ca componentDidUpdate
. De asemenea, useEffect
rulează întotdeauna prima dată, așa că funcționează ca și componentDidMount
.
Învechit în timp ce se reîmprospătează în Componentele funcției
Știm că useEffect
este similar cu metodele ciclului de viață al componentelor. Deci, putem modifica funcția de apel invers transmisă acesteia pentru a crea stocarea în cache învechită în timpul reîmprospătării pe care o aveam în componentele clasei. Totul rămâne la fel, cu excepția cârligului useEffect
.
const CACHE = {}; export default function Component({ page }) { const [users, setUsers] = useState([]); useEffect(() => { if (CACHE[page] !== undefined) { setUsers(CACHE[page]); } apiFetch(`https://reqres.in/api/users?page=${page}`).then(json => { CACHE[page] = json.data; setUsers(json.data); }); }, [page]); // ... create usersDOM from users return <div>{usersDOM}</div>; }
Astfel, avem cache învechite în timp ce reîmprospătează care funcționează într-o componentă de funcție.
Putem face același lucru pentru a doua componentă, adică să o convertim în funcționare și să implementăm memorarea în cache învechită în timp ce reîmprospăta. Rezultatul va fi identic cu cel pe care l-am avut la cursuri.
Dar asta nu este mai bun decât componentele de clasă, nu-i așa? Așadar, să vedem cum putem folosi puterea unui cârlig personalizat pentru a crea o logică modulară de reîmprospătare pe care o putem folosi pe mai multe componente.
Un cârlig personalizat învechit în timp ce se reîmprospătează
Mai întâi, să restrângem logica pe care dorim să o trecem într-un cârlig personalizat. Dacă te uiți la codul anterior, știi că este partea useState
și useEffect
. Mai precis, aceasta este logica pe care vrem să o modularizăm.
const [users, setUsers] = useState([]); useEffect(() => { if (CACHE[page] !== undefined) { setUsers(CACHE[page]); } apiFetch(`https://reqres.in/api/users?page=${page}`).then(json => { CACHE[page] = json.data; setUsers(json.data); }); }, [page]);
Deoarece trebuie să o facem generică, va trebui să facem URL-ul dinamic. Deci trebuie să avem url
-ul ca argument. Va trebui să actualizăm și logica de cache, deoarece cererile multiple pot avea aceeași valoare a page
. Din fericire, atunci când page
este inclusă cu adresa URL a punctului final, aceasta furnizează o valoare unică pentru fiecare solicitare unică. Deci, putem folosi întreaga adresă URL ca cheie pentru stocarea în cache:
const [data, setData] = useState([]); useEffect(() => { if (CACHE[url] !== undefined) { setData(CACHE[url]); } apiFetch(url).then(json => { CACHE[url] = json.data; setData(json.data); }); }, [url]);
Cam asta e tot. După ce l-am împachetat într-o funcție, vom avea cârligul nostru personalizat. Aruncă o privire mai jos.
const CACHE = {}; export default function useStaleRefresh(url, defaultValue = []) { const [data, setData] = useState(defaultValue); useEffect(() => { // cacheID is how a cache is identified against a unique request const cacheID = url; // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); } // fetch new data apiFetch(url).then(newData => { CACHE[cacheID] = newData.data; setData(newData.data); }); }, [url]); return data; }
Observați că i-am adăugat un alt argument numit defaultValue
. Valoarea implicită a unui apel API poate fi diferită dacă utilizați acest cârlig în mai multe componente. De aceea l-am făcut personalizabil.
Același lucru se poate face pentru cheia de data
din obiectul newData
. Dacă cârligul personalizat returnează o varietate de date, este posibil să doriți doar să returnați newData
și nu newData.data
și să gestionați acea traversare pe partea componentelor.
Acum că avem cârligul nostru personalizat, care face greutățile stocării în cache învechite în timp ce se reîmprospătează, iată cum îl conectăm la componentele noastre. Observați cantitatea mare de cod pe care am putut să o reducem. Întreaga noastră componentă este acum doar trei declarații. Este un mare câștig.
import useStaleRefresh from "./useStaleRefresh"; export default function Component({ page }) { const users = useStaleRefresh(`https://reqres.in/api/users?page=${page}`, []); const usersDOM = users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }
Putem face același lucru pentru a doua componentă. Va arata asa:
export default function Component2({ page }) { const cats = useStaleRefresh(`https://reqres.in/api/cats?page=${page}`, []); // ... create catsDOM from cats return <div>{catsDOM}</div>; }
Este ușor de văzut cât de mult cod boilerplate putem economisi dacă folosim acest cârlig. Codul arată și mai bine. Dacă doriți să vedeți întreaga aplicație în acțiune, mergeți la acest CodeSandbox.
Adăugarea unui indicator de încărcare pentru a useStaleRefresh
Acum că avem elementele de bază la punct, putem adăuga mai multe caracteristici la cârligul nostru personalizat. De exemplu, putem adăuga o valoare isLoading
în hook care este adevărată ori de câte ori este trimisă o solicitare unică și nu avem nicio memorie cache de afișat între timp.
Facem acest lucru având o stare separată pentru isLoading
și setând-o în funcție de starea cârligului. Adică, când nu este disponibil niciun conținut web stocat în cache, îl setăm la true
, în caz contrar îl setăm la false
.
Iată cârligul actualizat:
export default function useStaleRefresh(url, defaultValue = []) { const [data, setData] = useState(defaultValue); const [isLoading, setLoading] = useState(true); useEffect(() => { // cacheID is how a cache is identified against a unique request const cacheID = url; // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); setLoading(false); } else { // else make sure loading set to true setLoading(true); } // fetch new data apiFetch(url).then(newData => { CACHE[cacheID] = newData.data; setData(newData.data); setLoading(false); }); }, [url]); return [data, isLoading]; }
Acum putem folosi noua valoare isLoading
în componentele noastre.
export default function Component({ page }) { const [users, isLoading] = useStaleRefresh( `https://reqres.in/api/users?page=${page}`, [] ); if (isLoading) { return <div>Loading</div>; } // ... create usersDOM from users return <div>{usersDOM}</div>; }
Observați că odată ce ați terminat, vedeți textul „Încărcare” atunci când o solicitare unică este trimisă pentru prima dată și nu este prezentă nicio memorie cache.
Utilizarea suportului useStaleRefresh
Orice funcție async
Putem face hook-ul nostru personalizat și mai puternic, făcându-l să accepte orice funcție async
, mai degrabă decât doar cererile de rețea GET
. Ideea de bază din spatele ei va rămâne aceeași.
- În hook, apelați o funcție asincronă care returnează o valoare după ceva timp.
- Fiecare apel unic la o funcție asincronă este memorat în cache în mod corespunzător.
O simplă concatenare a function.name
și arguments
va funcționa ca o cheie cache pentru cazul nostru de utilizare. Folosind asta, așa va arăta cârligul nostru:
import { useState, useEffect, useRef } from "react"; import isEqual from "lodash/isEqual"; const CACHE = {}; export default function useStaleRefresh(fn, args, defaultValue = []) { const prevArgs = useRef(null); const [data, setData] = useState(defaultValue); const [isLoading, setLoading] = useState(true); useEffect(() => { // args is an object so deep compare to rule out false changes if (isEqual(args, prevArgs.current)) { return; } // cacheID is how a cache is identified against a unique request const cacheID = hashArgs(fn.name, ...args); // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); setLoading(false); } else { // else make sure loading set to true setLoading(true); } // fetch new data fn(...args).then(newData => { CACHE[cacheID] = newData; setData(newData); setLoading(false); }); }, [args, fn]); useEffect(() => { prevArgs.current = args; }); return [data, isLoading]; } function hashArgs(...args) { return args.reduce((acc, arg) => stringify(arg) + ":" + acc, ""); } function stringify(val) { return typeof val === "object" ? JSON.stringify(val) : String(val); }
După cum puteți vedea, folosim o combinație de numele funcției și argumentele sale șirizate pentru a identifica în mod unic un apel de funcție și, astfel, a-l stoca în cache. Acest lucru funcționează pentru aplicația noastră simplă, dar acest algoritm este predispus la coliziuni și comparații lente. (Cu argumente neserializabile, nu va funcționa deloc.) Deci, pentru aplicațiile din lumea reală, un algoritm de hashing adecvat este mai potrivit.
Un alt lucru de remarcat aici este utilizarea useRef
. useRef
este utilizat pentru a persista datele de-a lungul întregului ciclu de viață al componentei incluse. Deoarece args
este o matrice - care este un obiect în JavaScript - fiecare re-redare a componentei care utilizează cârligul face ca indicatorul de referință args
să se schimbe. Dar args
face parte din lista de dependențe în primul nostru useEffect
. Deci, schimbarea args
poate face ca useEffect
să ruleze chiar și atunci când nimic nu s-a schimbat. Pentru a contracara acest lucru, facem o comparație profundă între args
vechi și actuale folosind isEqual și lăsăm apelul useEffect
să ruleze numai dacă args
s-au schimbat efectiv.
Acum, putem folosi acest nou cârlig useStaleRefresh
după cum urmează. Observați schimbarea în defaultValue
aici. Deoarece este un cârlig de uz general, nu ne bazăm pe cârligul nostru pentru a returna cheia de data
în obiectul răspuns.
export default function Component({ page }) { const [users, isLoading] = useStaleRefresh( apiFetch, [`https://reqres.in/api/users?page=${page}`], { data: [] } ); if (isLoading) { return <div>Loading</div>; } const usersDOM = users.data.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }
Puteți găsi întregul cod în acest CodeSandbox.
Nu lăsați utilizatorii să aștepte: utilizați în mod eficient conținutul din cache cu Stale-while-refresh și React Hooks
Cârligul useStaleRefresh
pe care l-am creat în acest articol este o dovadă a conceptului care arată ce este posibil cu React Hooks. Încercați să vă jucați cu codul și vedeți dacă îl puteți încadra în aplicația dvs.
Alternativ, puteți încerca, de asemenea, să folosiți stale-while-refresh printr-o bibliotecă open-source populară, bine întreținută, cum ar fi swr sau react-query. Ambele sunt biblioteci puternice și acceptă o serie de funcții care ajută la solicitările API.
React Hooks schimbă jocul. Ele ne permit să împărtășim elegant logica componentelor. Acest lucru nu a fost posibil înainte, deoarece starea componentelor, metodele ciclului de viață și randarea au fost toate împachetate într-o singură entitate: componente de clasă. Acum, putem avea module diferite pentru toate. Acest lucru este excelent pentru compozibilitate și pentru scrierea unui cod mai bun. Folosesc componente de funcție și cârlige pentru tot noul cod React pe care îl scriu și recomand cu căldură acest lucru tuturor dezvoltatorilor React.