Cara Membuat Kait React Kustom Untuk Mengambil Dan Menyimpan Data
Diterbitkan: 2022-03-10componentDidMount()
, tetapi dengan pengenalan Hooks, Anda dapat membuat kait khusus yang akan mengambil dan menyimpan data untuk Anda. Itulah yang akan dibahas dalam tutorial ini.Jika Anda seorang pemula dalam React Hooks, Anda dapat memulai dengan memeriksa dokumentasi resmi untuk memahaminya. Setelah itu, saya akan merekomendasikan membaca "Memulai Dengan React Hooks API" dari Shedrack Akintyo. Untuk memastikan Anda mengikuti, ada juga artikel yang ditulis oleh Adeneye David Abiodun yang membahas praktik terbaik dengan React Hooks yang saya yakin akan berguna bagi Anda.
Sepanjang artikel ini, kita akan menggunakan Hacker News Search API untuk membuat custom hook yang bisa kita gunakan untuk mengambil data. Sementara tutorial ini akan membahas Hacker News Search API, kita akan memiliki hook yang berfungsi dengan cara yang akan mengembalikan respons dari tautan API valid yang kita berikan padanya.
Praktik Bereaksi Terbaik
React adalah library JavaScript yang fantastis untuk membangun antarmuka pengguna yang kaya. Ini menyediakan abstraksi komponen yang bagus untuk mengatur antarmuka Anda ke dalam kode yang berfungsi dengan baik, dan ada apa saja yang dapat Anda gunakan untuk itu. Baca artikel terkait di React →
Mengambil Data Dalam Komponen Bereaksi
Sebelum kait React, mengambil data awal dalam metode siklus hidup componentDidMount()
adalah konvensional, dan data berdasarkan perubahan prop atau status dalam metode siklus hidup componentDidUpdate()
.
Berikut cara kerjanya:
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(); } }
Metode siklus hidup componentDidMount
dipanggil segera setelah komponen dipasang, dan ketika itu selesai, yang kami lakukan adalah membuat permintaan untuk mencari "JavaScript" melalui Hacker News API dan memperbarui status berdasarkan respons.
Metode siklus hidup componentDidUpdate
, di sisi lain, dipanggil ketika ada perubahan dalam komponen. Kami membandingkan kueri sebelumnya dalam status dengan kueri saat ini untuk mencegah metode dipanggil setiap kali kami menetapkan "data" dalam status. Satu hal yang kita dapatkan dari menggunakan hook adalah menggabungkan kedua metode siklus hidup dengan cara yang lebih bersih — artinya kita tidak perlu memiliki dua metode siklus hidup saat komponen dipasang dan saat diperbarui.
Mengambil Data Dengan useEffect
Hook
Kait useEffect
dipanggil segera setelah komponen dipasang. Jika kita membutuhkan hook untuk dijalankan kembali berdasarkan beberapa perubahan prop atau state, kita harus meneruskannya ke array dependensi (yang merupakan argumen kedua dari hook useEffect
).
Mari kita jelajahi cara mengambil data dengan kait:
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]);
Dalam contoh di atas, kami meneruskan query
sebagai dependensi ke kait useEffect
kami. Dengan melakukan itu, kami memberi tahu useEffect
untuk melacak perubahan kueri. Jika nilai query
sebelumnya tidak sama dengan nilai saat ini, useEffect
akan dipanggil lagi.
Dengan demikian, kami juga menyetel beberapa status
pada komponen sesuai kebutuhan, karena ini akan menyampaikan beberapa pesan dengan lebih baik ke layar berdasarkan beberapa status
terbatas . Dalam keadaan tidak aktif , kami dapat memberi tahu pengguna bahwa mereka dapat menggunakan kotak telusur untuk memulai. Dalam status pengambilan , kami dapat menampilkan spinner . Dan, dalam keadaan diambil , kami akan merender data.
Penting untuk menyetel data sebelum Anda mencoba menyetel status ke fetched
sehingga Anda dapat mencegah kedipan yang terjadi akibat data kosong saat Anda menyetel status fetched
.
Membuat Kait Kustom
“Kait khusus adalah fungsi JavaScript yang namanya dimulai dengan 'penggunaan' dan yang mungkin memanggil Kait lain.”
— Bereaksi Dokumen
Itulah yang sebenarnya, dan bersama dengan fungsi JavaScript, ini memungkinkan Anda untuk menggunakan kembali beberapa bagian kode di beberapa bagian aplikasi Anda.
Definisi dari React Docs telah memberikannya tetapi mari kita lihat cara kerjanya dalam praktik dengan kait kustom penghitung:
const useCounter = (initialState = 0) => { const [count, setCount] = useState(initialState); const add = () => setCount(count + 1); const subtract = () => setCount(count - 1); return { count, add, subtract }; };
Di sini, kami memiliki fungsi reguler di mana kami mengambil argumen opsional, mengatur nilai ke status kami, serta menambahkan metode add
dan subtract
yang dapat digunakan untuk memperbaruinya.
Di mana-mana di aplikasi kami di mana kami membutuhkan penghitung, kami dapat memanggil useCounter
seperti fungsi biasa dan meneruskan initialState
sehingga kami tahu dari mana harus mulai menghitung. Ketika kami tidak memiliki status awal, kami default ke 0.
Berikut cara kerjanya dalam praktik:
import { useCounter } from './customHookPath'; const { count, add, subtract } = useCounter(100); eventHandler(() => { add(); // or subtract(); });
Apa yang kami lakukan di sini adalah mengimpor kait khusus kami dari file tempat kami mendeklarasikannya, sehingga kami dapat menggunakannya di aplikasi kami. Kami menetapkan status awalnya ke 100, jadi setiap kali kami memanggil add()
, itu menambah count
1, dan setiap kali kami memanggil subtract()
, itu mengurangi count
1.
Membuat useFetch
Hook
Sekarang setelah kita mempelajari cara membuat custom hook sederhana, mari ekstrak logika kita untuk mengambil data ke custom hook.
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 }; };
Ini hampir sama dengan yang kami lakukan di atas dengan pengecualian sebagai fungsi yang mengambil query
dan mengembalikan status
dan data
. Dan, itu adalah hook useFetch
yang bisa kita gunakan di beberapa komponen di aplikasi React kita.
Ini berfungsi, tetapi masalah dengan implementasi ini sekarang adalah, ini khusus untuk Hacker News jadi kami mungkin menyebutnya useHackerNews
. Apa yang ingin kami lakukan adalah, membuat kait useFetch
yang dapat digunakan untuk memanggil URL apa pun. Mari kita mengubahnya untuk mengambil URL sebagai gantinya!
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 }; };
Sekarang, kait useFetch kami bersifat generik dan kami dapat menggunakannya seperti yang kami inginkan di berbagai komponen kami.
Berikut salah satu cara mengkonsumsinya:
const [query, setQuery] = useState(''); const url = query && `https://hn.algolia.com/api/v1/search?query=${query}`; const { status, data } = useFetch(url);
Dalam hal ini, jika nilai query
adalah truthy
, kami melanjutkan untuk mengatur URL dan jika tidak, kami baik-baik saja dengan meneruskan undefined karena akan ditangani di hook kami. Efeknya akan mencoba berjalan sekali, terlepas dari itu.
Memoisasi Data yang Diambil
Memoisasi adalah teknik yang akan kami gunakan untuk memastikan bahwa kami tidak mencapai titik akhir hackernews
jika kami telah membuat semacam permintaan untuk mengambilnya pada beberapa fase awal. Menyimpan hasil panggilan pengambilan yang mahal akan menghemat waktu muat pengguna, oleh karena itu, meningkatkan kinerja secara keseluruhan.
Catatan : Untuk konteks lebih lanjut, Anda dapat melihat penjelasan Wikipedia tentang Memoisasi.
Mari kita jelajahi bagaimana kita bisa melakukannya!
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 }; };
Di sini, kami memetakan URL ke datanya. Jadi, jika kami membuat permintaan untuk mengambil beberapa data yang ada, kami mengatur data dari cache lokal kami, jika tidak, kami melanjutkan untuk membuat permintaan dan mengatur hasilnya di cache. Ini memastikan kami tidak melakukan panggilan API saat kami memiliki data yang tersedia untuk kami secara lokal. Kami juga akan melihat bahwa kami mematikan efek jika URL falsy
, jadi ini memastikan kami tidak melanjutkan untuk mengambil data yang tidak ada. Kita tidak bisa melakukannya sebelum hook useEffect
karena itu akan bertentangan dengan salah satu aturan hook, yaitu selalu memanggil hook di tingkat atas.
Mendeklarasikan cache
dalam lingkup yang berbeda berfungsi tetapi itu membuat kait kami bertentangan dengan prinsip fungsi murni. Selain itu, kami juga ingin memastikan bahwa React membantu membersihkan kekacauan kami ketika kami tidak ingin lagi menggunakan komponen tersebut. Kami akan menjelajahi useRef
untuk membantu kami mencapainya.
Memoizing Data Dengan useRef
“useRef
seperti sebuah kotak yang dapat menyimpan nilai yang dapat diubah di.current property
.”
— Bereaksi Dokumen
Dengan useRef
, kita dapat mengatur dan mengambil nilai yang dapat diubah dengan mudah dan nilainya tetap ada sepanjang siklus hidup komponen.
Mari kita ganti implementasi cache kita dengan sihir useRef
!
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 }; };
Di sini, cache kami sekarang di kait useFetch
kami dengan objek kosong sebagai nilai awal.
Membungkus
Yah, saya memang menyatakan bahwa menyetel data sebelum menyetel status yang diambil adalah ide yang bagus, tetapi ada dua masalah potensial yang dapat kita hadapi dengan itu juga:
- Pengujian unit kami dapat gagal karena larik data tidak kosong saat kami dalam status pengambilan. React sebenarnya dapat mengubah status batch tetapi tidak dapat melakukannya jika dipicu secara tidak sinkron;
- Aplikasi kami merender ulang lebih dari yang seharusnya.
Mari lakukan pembersihan terakhir pada kait useFetch
.,Kita akan mulai dengan mengalihkan useState
s ke useReducer
. Mari kita lihat cara kerjanya!
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);
Di sini, kami menambahkan status awal yang merupakan nilai awal yang kami berikan ke masing-masing useState
s individu kami. Dalam useReducer
kami, kami memeriksa jenis tindakan apa yang ingin kami lakukan, dan menetapkan nilai yang sesuai untuk dinyatakan berdasarkan itu.
Ini menyelesaikan dua masalah yang telah kita bahas sebelumnya, karena sekarang kita dapat mengatur status dan data pada saat yang sama untuk membantu mencegah keadaan yang tidak mungkin dan rendering ulang yang tidak perlu.
Hanya ada satu hal lagi yang tersisa: membersihkan efek samping kita. Fetch mengimplementasikan Promise API, dalam arti bahwa itu dapat diselesaikan atau ditolak. Jika pengait kami mencoba membuat pembaruan saat komponen telah dilepas karena beberapa Promise
baru saja diselesaikan, React akan mengembalikan Can't perform a React state update on an unmounted component.
Mari kita lihat bagaimana kita dapat memperbaikinya dengan pembersihan useEffect
!
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]);
Di sini, kami menyetel cancelRequest
ke true
setelah mendefinisikannya di dalam efek. Jadi, sebelum kami mencoba membuat perubahan status, kami mengonfirmasi terlebih dahulu apakah komponen telah di-unmount. Jika sudah di-unmount, kami melewatkan pembaruan status dan jika belum di-unmount, kami memperbarui status. Ini akan menyelesaikan kesalahan pembaruan status Bereaksi , dan juga mencegah kondisi balapan di komponen kami.
Kesimpulan
Kami telah menjelajahi beberapa konsep kait untuk membantu mengambil dan menyimpan data dalam komponen kami. Kami juga membersihkan kait useEffect
kami yang membantu mencegah sejumlah masalah di aplikasi kami.
Jika Anda memiliki pertanyaan, jangan ragu untuk menyampaikannya di bagian komentar di bawah!
- Lihat repo untuk artikel ini →
Referensi
- “Memperkenalkan Hooks,” React Docs
- “Memulai Dengan React Hooks API,” Shedrack Akintayo
- “Praktik Terbaik Dengan React Hooks,” Adeneye David Abiodun
- “Pemrograman Fungsional: Fungsi Murni,” Arne Brasseur