React Hooks ile Veri Getirme: Bir Kılavuz

Yayınlanan: 2022-03-11

Eskiyken yeniden doğrulama HTTP Cache-Control uzantısından yararlanmak popüler bir tekniktir. Önbellekte bulunurlarsa önbelleğe alınmış (eski) varlıkların kullanılmasını ve ardından önbelleğin yeniden doğrulanmasını ve gerekirse varlığın daha yeni bir sürümüyle güncellenmesini içerir. Bu nedenle, stale-while-revalidate adı.

stale-while-revalidate nasıl çalışır?

Bir istek ilk kez gönderildiğinde, tarayıcı tarafından önbelleğe alınır. Daha sonra aynı istek ikinci kez gönderildiğinde önce önbellek kontrol edilir. Bu isteğin önbelleği kullanılabilir ve geçerliyse, önbellek yanıt olarak döndürülür. Ardından, önbellek bayatlık açısından kontrol edilir ve eski bulunursa güncellenir. Bir önbelleğin bayatlığı, stale-while-revalidate ile birlikte Cache-Control başlığında bulunan max-age değeri tarafından belirlenir.

Eskimişken yeniden doğrulama mantığını izleyen bir akış şeması. Bir istekle başlar. Önbelleğe alınmamışsa veya önbellek geçersizse istek gönderilir, yanıt döndürülür ve önbellek güncellenir. Aksi takdirde, önbelleğe alınan yanıt döndürülür ve ardından önbellek bayatlık açısından kontrol edilir. Eskiyse, bir istek gönderilir ve önbellek güncellenir.

Bu, önbelleğe alınan varlıklar artık kritik yolda olmadığından hızlı sayfa yüklemelerine olanak tanır. Anında yüklenirler. Ayrıca geliştiriciler, önbelleğin ne sıklıkta kullanıldığını ve güncellendiğini kontrol ettiğinden, tarayıcıların kullanıcılara aşırı eski verileri göstermesini engelleyebilirler.

Okuyucular, sunucunun yanıtlarında belirli başlıkları kullanmasını ve tarayıcının oradan almasına izin verebilirlerse, önbelleğe alma için React ve Hooks kullanmaya ne gerek olduğunu düşünüyor olabilirler.

Sunucu ve tarayıcı yaklaşımının yalnızca statik içeriği önbelleğe almak istediğimizde işe yaradığı ortaya çıktı. Dinamik bir API için stale-while-revalidate kullanmaya ne dersiniz? Bu durumda, max-age ve stale-while-revalidate için iyi değerler bulmak zor. Genellikle, önbelleği geçersiz kılmak ve her istek gönderildiğinde yeni bir yanıt almak en iyi seçenek olacaktır. Bu, etkili bir şekilde önbelleğe alma olmadığı anlamına gelir. Ancak React ve Hooks ile daha iyisini yapabiliriz.

API için stale-while-revalidate

HTTP'nin stale-while-revalidate API çağrıları gibi dinamik isteklerle iyi çalışmadığını fark ettik.

Sonunda onu kullansak bile, tarayıcı ya önbelleği ya da yeni yanıtı döndürür, ikisini birden değil. Her istek gönderildiğinde yeni yanıtlar almak istediğimizden, bu bir API isteğiyle iyi gitmez. Ancak, yeni yanıtlar beklemek, uygulamanın anlamlı kullanılabilirliğini geciktirir.

Peki ne yapıyoruz?

Özel bir önbelleğe alma mekanizması uyguluyoruz. Bunun içinde, hem önbelleği hem de yeni yanıtı döndürmenin bir yolunu buluyoruz. Kullanıcı arabiriminde, önbelleğe alınan yanıt, kullanılabilir olduğunda yeni bir yanıtla değiştirilir. Mantık şu şekilde görünecektir:

  1. API sunucusu uç noktasına ilk kez bir istek gönderildiğinde, yanıtı önbelleğe alın ve ardından geri gönderin.
  2. Aynı API isteği bir daha gerçekleştiğinde, hemen önbelleğe alınmış yanıtı kullanın.
  3. Ardından, yeni bir yanıt almak için isteği eşzamansız olarak gönderin. Yanıt geldiğinde, değişiklikleri eşzamansız olarak kullanıcı arabirimine yayar ve önbelleği günceller.

Bu yaklaşım, her API isteği önbelleğe alındığından, anlık kullanıcı arabirimi güncellemelerine olanak tanır, ancak yeni yanıt verileri kullanılabilir olduğu anda görüntülendiğinden kullanıcı arabiriminde nihai doğruluk da sağlanır.

Bu eğitimde, bunun nasıl uygulanacağına dair adım adım bir yaklaşım göreceğiz. Yeni yanıt aldığında kullanıcı arayüzü gerçekten yenilendiğinden , bu yaklaşımı eski-yenileme olarak adlandıracağız.

Hazırlıklar: API

Bu öğreticiyi başlatmak için önce verileri alacağımız bir API'ye ihtiyacımız olacak. Neyse ki, bir sürü sahte API hizmeti mevcut. Bu eğitim için reqres.in kullanacağız.

Getirdiğimiz veriler, page sorgu parametresine sahip bir kullanıcı listesidir. Getirme kodu şöyle görünür:

 fetch("https://reqres.in/api/users?page=2") .then(res => res.json()) .then(json => { console.log(json); });

Bu kodu çalıştırmak bize aşağıdaki çıktıyı verir. İşte bunun tekrarlanmayan bir versiyonu:

 { 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 ] }

Bunun gerçek bir API gibi olduğunu görebilirsiniz. Yanıtta sayfalandırma var. page sorgulama parametresi sayfanın değiştirilmesinden sorumludur ve veri setinde toplam iki sayfamız bulunmaktadır.

API'yi bir React Uygulamasında Kullanma

API'yi bir React Uygulamasında nasıl kullandığımızı görelim. Nasıl yapılacağını öğrendikten sonra, önbelleğe alma kısmını anlayacağız. Bileşenimizi oluşturmak için bir sınıf kullanacağız. İşte kod:

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

Gerçek dünyadaki uygulamalarda sıklıkla olduğu gibi, page değerini props aracılığıyla aldığımıza dikkat edin. Ayrıca, this.props.page her değiştiğinde API verilerini yeniden getiren bir componentDidUpdate işlevimiz var.

Bu noktada, API sayfa başına altı öğe döndürdüğü için altı kullanıcılı bir liste gösterir:

React bileşen prototipimizin bir önizlemesi: her biri bir adın solunda bir fotoğrafa sahip altı ortalanmış çizgi.

Yenilenirken Eski Önbelleğe Alma Ekleme

Buna yenileme sırasında eski önbelleğe alma eklemek istiyorsak, uygulama mantığımızı şu şekilde güncellememiz gerekir:

  1. Bir isteğin yanıtını, ilk kez getirildikten sonra benzersiz şekilde önbelleğe alın.
  2. Bir isteğin önbelleği bulunursa, önbelleğe alınan yanıtı anında döndürün. Ardından, isteği gönderin ve yeni yanıtı eşzamansız olarak döndürün. Ayrıca, bir dahaki sefere bu yanıtı önbelleğe alın.

Bunu, önbelleği benzersiz bir şekilde depolayan global bir CACHE nesnesine sahip olarak yapabiliriz. Benzersizlik için, this.props.page değerini CACHE nesnemizde anahtar olarak kullanabiliriz. Ardından, yukarıda belirtilen algoritmayı basitçe kodluyoruz.

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

Önbellek bulunur bulunmaz döndürüldüğünden ve yeni yanıt verileri setState tarafından da döndürüldüğünden, bu, sorunsuz UI güncellemelerine sahip olduğumuz ve ikinci istekten itibaren uygulamada artık bekleme süremiz olmadığı anlamına gelir. Bu mükemmel ve kısaca yenilerken bayatlama yöntemi.

Yenileme sırasında eskime mantığını izleyen bir akış şeması. Bir istekle başlar. Önbelleğe alınmışsa, önbelleğe alınmış yanıtla setState() çağrılır. Her iki durumda da istek gönderilir, önbellek ayarlanır ve yeni bir yanıtla setState() çağrılır.

Buradaki apiFetch işlevi, gerçek zamanlı olarak önbelleğe almanın avantajını görebilmemiz fetch bir sarmalayıcı üzerinden getirmeden başka bir şey değildir. Bunu, API isteği tarafından döndürülen users listesine rastgele bir kullanıcı ekleyerek yapar. Ayrıca buna rastgele bir gecikme ekler:

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

Buradaki getFakeUser() işlevi, sahte bir kullanıcı nesnesi oluşturmaktan sorumludur.

Bu değişikliklerle API'miz eskisinden daha gerçek.

  1. Yanıt vermede rastgele bir gecikme var.
  2. Aynı istekler için biraz farklı veriler döndürür.

Bunu göz önünde bulundurarak, ana bileşenimizden Component iletilen page desteğini değiştirdiğimizde, API önbelleğe alma işlemini iş başında görebiliriz. Bu CodeSandbox'ta birkaç saniyede bir Geçiş Yap düğmesini tıklamayı deneyin ve şöyle bir davranış görmelisiniz:

Önbelleğe almanın etkin olduğu geçiş sayfasını gösteren bir animasyon. Ayrıntılar makalede açıklanmıştır.

Yakından bakarsanız, birkaç şey olur.

  1. Uygulama başladığında ve varsayılan durumundayken, yedi kullanıcıdan oluşan bir liste görüyoruz. Bu istek gönderildiğinde rastgele değiştirilecek olan kullanıcı olduğu için listedeki son kullanıcıyı not edin.
  2. Toggle'a ilk tıkladığımızda kısa bir süre (400-700ms) bekleyip listeyi bir sonraki sayfaya güncelliyor.
  3. Şimdi ikinci sayfadayız. Listedeki son kullanıcıyı tekrar not alın.
  4. Şimdi tekrar Toggle'a tıklıyoruz ve uygulama ilk sayfaya geri dönecek. Şimdi son girişin hala 1. Adımda not ettiğimiz kullanıcıyla aynı olduğuna ve daha sonra yeni (rastgele) kullanıcıya değiştiğine dikkat edin. Bunun nedeni, başlangıçta önbelleğin gösterilmesi ve ardından asıl yanıtın devreye girmesidir.
  5. Tekrar Toggle'a tıklıyoruz. Aynı fenomen olur. Son seferden önbelleğe alınan yanıt anında yüklenir ve ardından yeni veriler getirilir ve böylece 3. Adımda not ettiğimizden son giriş güncellemesini görürüz.

İşte bu, aradığımız eski yenileme sırasında önbelleğe alma. Ancak bu yaklaşım, bir kod çoğaltma sorunundan muzdariptir. Önbelleğe alma ile başka bir veri getirme bileşenimiz varsa nasıl gittiğini görelim. Bu bileşen, öğeleri ilk bileşenimizden farklı gösterir.

Yenileme sırasında Eskimeyi Başka Bir Bileşene Ekleme

Bunu, mantığı ilk bileşenden kopyalayarak yapabiliriz. İkinci bileşenimiz kedilerin bir listesini gösterir:

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

Gördüğünüz gibi, burada yer alan bileşen mantığı, ilk bileşenle hemen hemen aynı. Tek fark, istenen uç noktada ve liste öğelerini farklı göstermesidir.

Şimdi bu iki bileşeni yan yana gösteriyoruz. Benzer şekilde davrandıklarını görebilirsiniz:

Yan yana iki bileşenle geçişi gösteren bir animasyon.

Bu sonucu elde etmek için çok fazla kod çoğaltma yapmak zorunda kaldık. Bunun gibi birden fazla bileşenimiz olsaydı, çok fazla kod kopyalıyor olurduk.

Bunu yinelenmeyen bir şekilde çözmek için, verileri alıp önbelleğe almak ve sahne olarak iletmek için Daha Yüksek Dereceli bir Bileşene sahip olabiliriz. İdeal değil ama işe yarayacak. Ancak, tek bir bileşende birden fazla istek yapmak zorunda kalsaydık, birden fazla Yüksek Dereceli Bileşene sahip olmak çok çabuk çirkinleşirdi.

Ardından, muhtemelen bunu sınıf bileşenlerinde yapmanın en iyi yolu olan render props modelimiz var. Mükemmel çalışıyor, ancak yine de “cehennemi sarmaya” eğilimlidir ve zaman zaman mevcut bağlamı bağlamamızı gerektirir. Bu harika bir geliştirici deneyimi değildir ve hayal kırıklığına ve hatalara yol açabilir.

React Hooks'un günü kurtardığı yer burasıdır. Bileşen mantığını birden fazla yerde kullanabilmemiz için yeniden kullanılabilir bir kapta kutulamamıza izin veriyorlar. React Hooks, React 16.8'de tanıtıldı ve yalnızca işlev bileşenleriyle çalışırlar. React önbellek kontrolüne, özellikle de içeriği Hooks ile önbelleğe almaya başlamadan önce, işlev bileşenlerinde basit veri getirmeyi nasıl yaptığımızı görelim.

İşlev Bileşenlerinde API Verisi Alma

API verilerini işlev bileşenlerinde almak için useState ve useEffect kancalarını kullanırız.

useState , sınıf bileşenlerinin state ve setState ile benzerdir. Bu kancayı, bir fonksiyon bileşeni içinde atomik durum kaplarına sahip olmak için kullanırız.

useEffect bir yaşam döngüsü kancasıdır ve bunu componentDidMount , componentDidUpdate ve componentWillUnmount kombinasyonu olarak düşünebilirsiniz. useEffect iletilen ikinci parametreye bağımlılık dizisi denir. Bağımlılık dizisi değiştiğinde, useEffect ilk argüman olarak geçirilen geri arama yeniden çalıştırılır.

Veri getirmeyi uygulamak için bu kancaları şu şekilde kullanacağız:

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

page useEffect bağımlılık olarak belirterek, React'e page her değiştirildiğinde useEffect geri çağrımızı çalıştırmasını söyleriz. Bu, componentDidUpdate gibidir. Ayrıca, useEffect her zaman ilk seferde çalışır ve bu nedenle componentDidMount gibi çalışır.

İşlev Bileşenlerinde yenileme sırasında eskime

useEffect bileşen yaşam döngüsü yöntemlerine benzer olduğunu biliyoruz. Böylece, sınıf bileşenlerinde sahip olduğumuz eski yenileme sırasında önbelleğe almayı oluşturmak için kendisine iletilen geri arama işlevini değiştirebiliriz. useEffect kancası dışında her şey aynı kalır.

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

Böylece, bir işlev bileşeninde çalışan yenilenirken eski önbelleğe alma işlemine sahibiz.

Aynısını ikinci bileşen için de yapabiliriz, yani onu işleve dönüştürebilir ve yenilenirken eski önbelleğe almayı uygulayabiliriz. Sonuç, derslerde sahip olduğumuzla aynı olacaktır.

Ama bu, sınıf bileşenlerinden daha iyi değil, değil mi? Şimdi birden çok bileşende kullanabileceğimiz modüler yenileme sırasında eskime mantığı oluşturmak için özel bir kancanın gücünü nasıl kullanabileceğimizi görelim.

Yenileme sırasında özel bir bayatlama kancası

İlk olarak, özel bir kancaya taşımak istediğimiz mantığı daraltalım. Önceki koda bakarsanız, bunun useState ve useEffect kısmı olduğunu bilirsiniz. Daha spesifik olarak, modülerleştirmek istediğimiz mantık budur.

 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]);

Genel yapmamız gerektiğinden, URL'yi dinamik yapmamız gerekecek. Bu yüzden argüman olarak url ihtiyacımız var. Birden çok istek aynı page değerine sahip olabileceğinden, önbelleğe alma mantığını da güncellememiz gerekecek. Neyse ki, page uç nokta URL'sine eklendiğinde, her benzersiz istek için benzersiz bir değer verir. Böylece URL'nin tamamını önbelleğe alma için bir anahtar olarak kullanabiliriz:

 const [data, setData] = useState([]); useEffect(() => { if (CACHE[url] !== undefined) { setData(CACHE[url]); } apiFetch(url).then(json => { CACHE[url] = json.data; setData(json.data); }); }, [url]);

Hepsi bukadar. Bir fonksiyonun içine sardıktan sonra özel kancamız olacak. Aşağıda bir göz atın.

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

Buna defaultValue adında başka bir argüman eklediğimize dikkat edin. Bu kancayı birden çok bileşende kullanırsanız, bir API çağrısının varsayılan değeri farklı olabilir. Bu yüzden özelleştirilebilir hale getirdik.

newData nesnesindeki data anahtarı için de yapılabilir. Özel kancanız çeşitli veriler döndürüyorsa, newData.data yerine yalnızca newData döndürmek ve bu geçişi bileşen tarafında ele almak newData.data .

Artık yenilenirken bayatlayan önbelleğe almanın ağır yükünü kaldıran özel kancamıza sahip olduğumuza göre, onu bileşenlerimize nasıl taktığımız aşağıda açıklanmıştır. Azaltmayı başardığımız kod miktarına dikkat edin. Tüm bileşenimiz artık sadece üç ifadedir. Bu büyük bir kazanç.

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

Aynı işlemi ikinci bileşen için de yapabiliriz. Bunun gibi görünecek:

 export default function Component2({ page }) { const cats = useStaleRefresh(`https://reqres.in/api/cats?page=${page}`, []); // ... create catsDOM from cats return <div>{catsDOM}</div>; }

Bu kancayı kullanırsak ne kadar ortak koddan tasarruf edebileceğimizi görmek kolaydır. Kod da daha iyi görünüyor. Tüm uygulamayı çalışırken görmek istiyorsanız, bu CodeSandbox'a gidin.

useStaleRefresh kullanmak için bir Yükleme Göstergesi Ekleme

Artık temel bilgilere sahip olduğumuza göre, özel kancamıza daha fazla özellik ekleyebiliriz. Örneğin, benzersiz bir istek gönderildiğinde doğru olan kancaya bir isLoading değeri ekleyebiliriz ve bu arada gösterecek önbelleğimiz yok.

Bunu isLoading için ayrı bir durum alarak ve onu kancanın durumuna göre ayarlayarak yapıyoruz. Yani, önbelleğe alınmış web içeriği olmadığında, onu true olarak, aksi takdirde false olarak ayarlarız.

İşte güncellenmiş kanca:

 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]; }

Artık bileşenlerimizde yeni isLoading değerini kullanabiliriz.

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

Bunu yaptığınızda, benzersiz bir istek ilk kez gönderildiğinde ve önbellek bulunmadığında “Yükleniyor” metnini gördüğünüze dikkat edin.

Uygulanan bir yükleme göstergesiyle bileşeni gösteren bir animasyon.

useStaleRefresh Herhangi bir zaman async İşlevi Destekleyin

Özel kancamızı, yalnızca GET ağ istekleri yerine herhangi bir zaman async işlevi desteklemesini sağlayarak daha da güçlü hale getirebiliriz. Arkasındaki temel fikir aynı kalacaktır.

  1. Kancada, bir süre sonra bir değer döndüren bir zaman uyumsuz işlevi çağırırsınız.
  2. Bir zaman uyumsuz işlevine yapılan her benzersiz çağrı uygun şekilde önbelleğe alınır.

function.name ve arguments basit bir birleşimi, kullanım durumumuz için bir önbellek anahtarı olarak çalışacaktır. Bunu kullanarak, kancamız şöyle görünecek:

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

Gördüğünüz gibi, bir işlev çağrısını benzersiz bir şekilde tanımlamak ve böylece onu önbelleğe almak için işlev adı ve dizgeli argümanlarının bir kombinasyonunu kullanıyoruz. Bu, basit uygulamamız için işe yarar, ancak bu algoritma çarpışmalara ve yavaş karşılaştırmalara eğilimlidir. (Serileştirilemeyen argümanlarla hiç çalışmayacaktır.) Dolayısıyla gerçek dünyadaki uygulamalar için uygun bir karma algoritma daha uygundur.

Burada dikkat edilmesi gereken bir diğer nokta da useRef kullanımıdır. useRef , verileri çevreleyen bileşenin tüm yaşam döngüsü boyunca sürdürmek için kullanılır. args , JavaScript'te bir nesne olan bir dizi olduğundan, bileşenin kancayı kullanarak her yeniden oluşturulması, args referans işaretçisinin değişmesine neden olur. Ancak args , ilk useEffect bağımlılık listesinin bir parçasıdır. Bu nedenle, argümanların değiştirilmesi, hiçbir şey değişmediğinde bile args çalışmasını useEffect . Buna karşı koymak için, isEqual kullanarak eski ve mevcut args arasında derin bir karşılaştırma yapıyoruz ve sadece args gerçekten değiştiyse useEffect geri çağrısının çalışmasına izin veriyoruz.

Şimdi bu yeni useStaleRefresh kancasını aşağıdaki gibi kullanabiliriz. Burada defaultValue değişikliğe dikkat edin. Genel amaçlı bir kanca olduğundan, yanıt nesnesindeki data anahtarını döndürmek için kancamıza güvenmiyoruz.

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

Kodun tamamını bu CodeSandbox'ta bulabilirsiniz.

Kullanıcıları Bekletmeyin: Önbellek İçeriğini Yenilerken Eskitme ve Tepki Kancaları ile Etkili Bir Şekilde Kullanın

Bu makalede oluşturduğumuz useStaleRefresh kancası, React Hooks ile nelerin mümkün olduğunu gösteren bir kavram kanıtıdır. Kodla oynamayı deneyin ve uygulamanıza sığdırıp sığamayacağınıza bakın.

Alternatif olarak, swr veya tepki sorgusu gibi popüler, bakımlı bir açık kaynak kitaplığı aracılığıyla yenilenirken bayatlamadan yararlanmayı da deneyebilirsiniz. Her ikisi de güçlü kitaplıklardır ve API isteklerine yardımcı olan bir dizi özelliği destekler.

React Hooks oyunun kurallarını değiştirir. Bileşen mantığını zarif bir şekilde paylaşmamıza izin veriyorlar. Bu daha önce mümkün değildi çünkü bileşen durumu, yaşam döngüsü yöntemleri ve işleme tek bir varlıkta paketlendi: sınıf bileşenleri. Artık hepsi için farklı modüllerimiz olabilir. Bu, birleştirilebilirlik ve daha iyi kod yazmak için harikadır. Yazdığım tüm yeni React kodları için fonksiyon bileşenleri ve kancalar kullanıyorum ve bunu tüm React geliştiricilerine şiddetle tavsiye ediyorum.

İlgili: Redux Araç Takımı ve RTK Sorgusu ile React Uygulamaları Oluşturma