Verileri Almak ve Önbelleğe Almak İçin Özel Bir Tepki Kancası Nasıl Oluşturulur

Yayınlanan: 2022-03-10
Hızlı özet ↬ React uygulamanızdaki birçok bileşenin, kullanıcılarınıza gösterilecek verileri almak için bir API'ye çağrı yapmak zorunda kalma olasılığı yüksektir. Bunu componentDidMount() yaşam döngüsü yöntemini kullanarak yapmak zaten mümkün, ancak Hooks'un kullanıma sunulmasıyla, verileri sizin için alıp önbelleğe alacak özel bir kanca oluşturabilirsiniz. Bu eğitimin kapsayacağı şey bu.

React Hooks'ta yeniyseniz, onu anlamak için resmi belgeleri kontrol ederek başlayabilirsiniz. Ondan sonra Shedrack Akintayo'nun “Getting Started With React Hooks API” kitabını okumanızı tavsiye ederim. Takip ettiğinizden emin olmak için, Adeneye David Abiodun tarafından yazılmış, React Hooks ile ilgili en iyi uygulamaları kapsayan ve sizin için faydalı olacağına eminim.

Bu makale boyunca, verileri getirmek için kullanabileceğimiz özel bir kanca oluşturmak için Hacker News Search API'sini kullanacağız. Bu eğitici, Hacker News Search API'sini kapsayacak olsa da, kancanın, ona ilettiğimiz herhangi bir geçerli API bağlantısından yanıt döndürecek şekilde çalışmasını sağlayacağız.

En İyi Tepki Uygulamaları

React, zengin kullanıcı arayüzleri oluşturmak için harika bir JavaScript kitaplığıdır. Arayüzlerinizi iyi işleyen kod halinde düzenlemek için harika bir bileşen soyutlaması sağlar ve onu kullanabileceğiniz hemen hemen her şey vardır. React ile ilgili bir makaleyi okuyun →

Bir React Bileşeninde Veri Getirme

React kancalarından önce, componentDidMount() yaşam döngüsü yönteminde ilk verileri ve componentDidMount() componentDidUpdate() döngüsü yönteminde prop veya durum değişikliklerine dayalı verileri getirmek gelenekseldi.

İşte nasıl çalıştığı:

 componentDidMount() { const fetchData = async () => { const response = await fetch( `https://hn.algolia.com/api/v1/search?query=JavaScript` ); const data = await response.json(); this.setState({ data }); }; fetchData(); } componentDidUpdate(previousProps, previousState) { if (previousState.query !== this.state.query) { const fetchData = async () => { const response = await fetch( `https://hn.algolia.com/api/v1/search?query=${this.state.query}` ); const data = await response.json(); this.setState({ data }); }; fetchData(); } }

Bileşen monte edilir edilmez componentDidMount yaşam döngüsü yöntemi çağrılır ve bu yapıldığında, yaptığımız şey Hacker News API aracılığıyla “JavaScript” aramak ve yanıta göre durumu güncellemek için bir istekte bulunmaktı.

Öte yandan componentDidUpdate yaşam döngüsü yöntemi, bileşende bir değişiklik olduğunda çağrılır. Durumdaki "veri"yi her ayarladığımızda yöntemin çağrılmasını önlemek için önceki sorguyu mevcut sorguyla karşılaştırdık. Kancaları kullanmaktan elde ettiğimiz bir şey, her iki yaşam döngüsü yöntemini daha temiz bir şekilde birleştirmektir - bu, bileşenin ne zaman monte edildiği ve ne zaman güncellendiği için iki yaşam döngüsü yöntemine ihtiyacımız olmayacağı anlamına gelir.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

useEffect Hook ile Veri Alma

useEffect kancası, bileşen monte edilir edilmez çağrılır. Bazı prop veya durum değişikliklerine dayalı olarak kancanın yeniden çalıştırılmasına ihtiyacımız varsa, bunları bağımlılık dizisine geçirmemiz gerekir (bu, useEffect kancasının ikinci argümanıdır).

Kancalarla nasıl veri getirileceğini keşfedelim:

 import { useState, useEffect } from 'react'; const [status, setStatus] = useState('idle'); const [query, setQuery] = useState(''); const [data, setData] = useState([]); useEffect(() => { if (!query) return; const fetchData = async () => { setStatus('fetching'); const response = await fetch( `https://hn.algolia.com/api/v1/search?query=${query}` ); const data = await response.json(); setData(data.hits); setStatus('fetched'); }; fetchData(); }, [query]);

Yukarıdaki örnekte, useEffect bir bağımlılık olarak query geçtik. Bunu yaparak, useEffect sorgu değişikliklerini izlemesini söylüyoruz. Önceki query değeri geçerli değerle aynı değilse, useEffect yeniden çağrılır.

Bununla birlikte, bazı sonlu durum status dayalı olarak ekrana bazı mesajları daha iyi ileteceğinden, gerektiğinde bileşen üzerinde birkaç status ayarlıyoruz. Boş durumdayken, kullanıcılara başlamak için arama kutusunu kullanabileceklerini bildirebiliriz. Getirme durumunda, bir döndürücü gösterebiliriz. Ve alınan durumda, verileri işleyeceğiz .

Durumu fetched olarak ayarlamaya çalışmadan önce verileri ayarlamanız önemlidir, böylece fetched ​​durumunu ayarlarken verilerin boş olması nedeniyle oluşan bir titremeyi önleyebilirsiniz.

Özel Kanca Oluşturma

"Özel bir kanca, adı 'kullanım' ile başlayan ve diğer Kancaları çağırabilen bir JavaScript işlevidir."

— Tepki Belgeleri

Gerçekte olan budur ve bir JavaScript işleviyle birlikte, uygulamanızın çeşitli bölümlerinde bazı kod parçalarını yeniden kullanmanıza olanak tanır.

React Docs'taki tanım onu ​​ele verdi, ancak pratikte karşı özel bir kanca ile nasıl çalıştığını görelim:

 const useCounter = (initialState = 0) => { const [count, setCount] = useState(initialState); const add = () => setCount(count + 1); const subtract = () => setCount(count - 1); return { count, add, subtract }; };

Burada, isteğe bağlı bir argüman aldığımız, değeri durumumuza ayarladığımız ve güncellemek için kullanılabilecek toplama ve subtract yöntemlerini add normal bir fonksiyonumuz var.

Uygulamamızda bir sayaca ihtiyacımız olan her yerde, normal bir işlev gibi useCounter çağırabilir ve nereden saymaya başlayacağımızı bilmemiz için bir initialState iletebiliriz. Bir başlangıç ​​durumumuz olmadığında, varsayılan olarak 0'a alırız.

Pratikte şu şekilde çalışır:

 import { useCounter } from './customHookPath'; const { count, add, subtract } = useCounter(100); eventHandler(() => { add(); // or subtract(); });

Burada yaptığımız, özel kancamızı, onu uygulamamızda kullanabilmemiz için içinde bildirdiğimiz dosyadan içe aktarmaktı. İlk durumunu 100'e ayarladık, bu nedenle ne zaman add() çağırsak, count 1 artırır ve subtract() her çağırdığımızda, count 1 azaltır.

useFetch Kancası oluşturma

Artık basit bir özel kanca oluşturmayı öğrendiğimize göre, verileri özel bir kancaya getirmek için mantığımızı çıkaralım.

 const useFetch = (query) => { const [status, setStatus] = useState('idle'); const [data, setData] = useState([]); useEffect(() => { if (!query) return; const fetchData = async () => { setStatus('fetching'); const response = await fetch( `https://hn.algolia.com/api/v1/search?query=${query}` ); const data = await response.json(); setData(data.hits); setStatus('fetched'); }; fetchData(); }, [query]); return { status, data }; };

query alan ve status ve data döndüren bir işlev olması dışında, yukarıda yaptığımızla hemen hemen aynı. Ve bu, React uygulamamızdaki çeşitli bileşenlerde kullanabileceğimiz bir useFetch kancası.

Bu işe yarar, ancak bu uygulamadaki sorun şu ki, Hacker News'e özgüdür, bu yüzden onu useHackerNews olarak adlandırabiliriz. Yapmayı düşündüğümüz şey, herhangi bir URL'yi çağırmak için kullanılabilecek bir useFetch kancası oluşturmaktır. Bunun yerine bir URL'yi alacak şekilde yenileyelim!

 const useFetch = (url) => { const [status, setStatus] = useState('idle'); const [data, setData] = useState([]); useEffect(() => { if (!url) return; const fetchData = async () => { setStatus('fetching'); const response = await fetch(url); const data = await response.json(); setData(data); setStatus('fetched'); }; fetchData(); }, [url]); return { status, data }; };

Artık useFetch kancamız geneldir ve onu çeşitli bileşenlerimizde istediğimiz gibi kullanabiliriz.

İşte onu tüketmenin bir yolu:

 const [query, setQuery] = useState(''); const url = query && `https://hn.algolia.com/api/v1/search?query=${query}`; const { status, data } = useFetch(url);

Bu durumda, query değeri truthy ise, URL'yi ayarlamaya devam ederiz ve değilse, kancamıza işleneceği için undefined iletmekte sorun yok. Efekt, ne olursa olsun bir kez çalışmaya çalışacaktır.

Getirilen Verileri Not Alma

Notlandırma, bir başlangıç ​​aşamasında onu almak için bir tür istekte bulunduysak, hackernews son noktasına ulaşmamamızı sağlamak için kullanacağımız bir tekniktir. Pahalı getirme çağrılarının sonucunu saklamak, kullanıcılara yükleme süresinden tasarruf sağlayacak ve bu nedenle genel performansı artıracaktır.

Not : Daha fazla bağlam için Wikipedia'nın Memoization hakkındaki açıklamasına göz atabilirsiniz.

Bunu nasıl yapabileceğimizi keşfedelim!

 const cache = {}; const useFetch = (url) => { const [status, setStatus] = useState('idle'); const [data, setData] = useState([]); useEffect(() => { if (!url) return; const fetchData = async () => { setStatus('fetching'); if (cache[url]) { const data = cache[url]; setData(data); setStatus('fetched'); } else { const response = await fetch(url); const data = await response.json(); cache[url] = data; // set response in cache; setData(data); setStatus('fetched'); } }; fetchData(); }, [url]); return { status, data }; };

Burada, URL'leri verileriyle eşleştiriyoruz. Bu nedenle, mevcut bazı verileri almak için bir istek yaparsak, verileri yerel önbelleğimizden ayarlarız, aksi takdirde istekte bulunmaya devam eder ve sonucu önbellekte ayarlarız. Bu, yerel olarak elimizde mevcut veriler olduğunda bir API çağrısı yapmamamızı sağlar. Ayrıca, URL falsy ise etkiyi ortadan kaldırdığımızı fark edeceğiz, bu nedenle var olmayan verileri getirmeye devam etmememizi sağlar. Bunu useEffect kancasından önce yapamayız çünkü bu, kancaların kurallarından birine, yani kancaları her zaman en üst düzeyde çağırmak anlamına gelir.

cache farklı bir kapsamda bildirmek işe yarar, ancak kancamızı saf işlev ilkesine aykırı hale getirir. Ayrıca, bileşeni artık kullanmak istemediğimizde React'in dağınıklığı temizlememize yardımcı olduğundan da emin olmak istiyoruz. Bunu başarmamıza yardımcı olması için useRef keşfedeceğiz.

useRef ile Verileri Hafızaya Alma

useRef , .current property değişken bir değer tutabilen bir kutu gibidir.”

— Tepki Belgeleri

useRef ile değiştirilebilir değerleri kolaylıkla ayarlayabilir ve alabiliriz ve değeri, bileşenin yaşam döngüsü boyunca devam eder.

Önbellek uygulamamızı biraz useRef büyüsü ile değiştirelim!

 const useFetch = (url) => { const cache = useRef({}); const [status, setStatus] = useState('idle'); const [data, setData] = useState([]); useEffect(() => { if (!url) return; const fetchData = async () => { setStatus('fetching'); if (cache.current[url]) { const data = cache.current[url]; setData(data); setStatus('fetched'); } else { const response = await fetch(url); const data = await response.json(); cache.current[url] = data; // set response in cache; setData(data); setStatus('fetched'); } }; fetchData(); }, [url]); return { status, data }; };

Burada önbelleğimiz artık başlangıç ​​değeri olarak boş bir nesneyle useFetch .

Toplama

Getirilen durumu ayarlamadan önce verileri ayarlamanın iyi bir fikir olduğunu belirtmiştim, ancak bununla ilgili iki olası sorun da olabilir:

  1. Alma durumundayken veri dizisinin boş olmaması nedeniyle birim testimiz başarısız olabilir. React, durum değişikliklerini toplu olarak yapabilir, ancak eşzamansız olarak tetiklenirse bunu yapamaz;
  2. Uygulamamız olması gerekenden daha fazlasını yeniden oluşturuyor.

useFetch kancamızda son bir temizlik yapalım., useState ' useState useFetch değiştirerek useReducer . Bunun nasıl çalıştığını görelim!

 const initialState = { status: 'idle', error: null, data: [], }; const [state, dispatch] = useReducer((state, action) => { switch (action.type) { case 'FETCHING': return { ...initialState, status: 'fetching' }; case 'FETCHED': return { ...initialState, status: 'fetched', data: action.payload }; case 'FETCH_ERROR': return { ...initialState, status: 'error', error: action.payload }; default: return state; } }, initialState);

Burada, bireysel useState her birine ilettiğimiz başlangıç ​​değeri olan bir başlangıç ​​durumu ekledik. useReducer , ne tür bir eylem gerçekleştirmek istediğimizi kontrol eder ve buna göre duruma uygun değerleri belirleriz.

Bu, daha önce tartıştığımız iki sorunu çözüyor, çünkü artık imkansız durumları ve gereksiz yeniden oluşturmaları önlemeye yardımcı olmak için durumu ve verileri aynı anda ayarlayabiliyoruz.

Geriye bir şey daha kaldı: yan etkimizi temizlemek. Fetch, çözülebileceği veya reddedilebileceği anlamında Promise API'sini uygular. Eğer Promise , bazı Sözler çözüldüğü için bileşen bağlantısı kaldırılmışken bir güncelleme yapmaya çalışırsa, React, Monte Can't perform a React state update on an unmounted component.

Bunu useEffect temizliği ile nasıl düzeltebileceğimizi görelim!

 useEffect(() => { let cancelRequest = false; if (!url) return; const fetchData = async () => { dispatch({ type: 'FETCHING' }); if (cache.current[url]) { const data = cache.current[url]; dispatch({ type: 'FETCHED', payload: data }); } else { try { const response = await fetch(url); const data = await response.json(); cache.current[url] = data; if (cancelRequest) return; dispatch({ type: 'FETCHED', payload: data }); } catch (error) { if (cancelRequest) return; dispatch({ type: 'FETCH_ERROR', payload: error.message }); } } }; fetchData(); return function cleanup() { cancelRequest = true; }; }, [url]);

Burada, efektin içinde tanımladıktan sonra cancelRequest true olarak ayarlıyoruz. Bu nedenle, durum değişiklikleri yapmaya çalışmadan önce, bileşenin sökülüp takılmadığını onaylarız. Demonte edilmişse durumu güncellemeyi atlıyoruz ve eğer sökülmemişse durumu güncelliyoruz. Bu, React durumu güncelleme hatasını çözecek ve ayrıca bileşenlerimizdeki yarış koşullarını önleyecektir.

Çözüm

Bileşenlerimizde verilerin alınmasına ve önbelleğe alınmasına yardımcı olacak birkaç kanca konseptini araştırdık. Ayrıca, uygulamamızda çok sayıda sorunu önlemeye yardımcı olan useEffect da temizledik.

Herhangi bir sorunuz varsa, lütfen bunları aşağıdaki yorumlar bölümüne bırakmaktan çekinmeyin!

  • Bu makale için depoya bakın →

Referanslar

  • "Kancalarla Tanışın", React Docs
  • "React Hooks API'sine Başlarken," Shedrack Akintayo
  • “React Hooks ile En İyi Uygulamalar,” Adeneye David Abiodun
  • “Fonksiyonel Programlama: Saf Fonksiyonlar,” Arne Brasseur