Normalisasi otomatis dan pembaruan data untuk pustaka pengambilan data (react-query, swr, rtk-query, dan lainnya)
Perkenalan
Motivasi
Instalasi
Kondisi yang diperlukan
Normalisasi array
Men-debug
Pertunjukan
Integrasi
Contoh
normy
adalah perpustakaan, yang memungkinkan data aplikasi Anda dinormalisasi secara otomatis. Kemudian, setelah data dinormalisasi, dalam banyak kasus, data Anda dapat diperbarui secara otomatis.
Inti dari normy
- yaitu perpustakaan @normy/core
, yang tidak dimaksudkan untuk digunakan secara langsung dalam aplikasi, memiliki logika di dalamnya yang memungkinkan integrasi yang mudah dengan perpustakaan pengambilan data favorit Anda. Sudah ada integrasi resmi dengan react-query
, swr
dan RTK Query
. Jika Anda menggunakan perpustakaan pengambilan lain, Anda dapat mengangkat masalah Github, sehingga masalah tersebut dapat ditambahkan juga.
Untuk memahami apa yang sebenarnya dilakukan normy
, yang terbaik adalah melihat contohnya. Anggaplah Anda menggunakan react-query
. Kemudian Anda dapat memfaktorkan ulang kode dengan cara berikut:
impor Bereaksi dari 'bereaksi'; impor { Penyedia Klien Kueri, Klien Kueri, gunakanQueryClient, } dari '@tanstack/react-query';+ impor { QueryNormalizerProvider } dari '@normy/react-query'; const queryClient = QueryClient baru(); const Buku = () => { const queryClient = useQueryClient(); const { data: booksData = [] } = useQuery(['buku'], () => Janji.resolve([ { id: '1', nama: 'Nama 1', penulis: { id: '1001', nama: 'Pengguna1' } }, { id: '2', nama: 'Nama 2', penulis: { id: '1002', nama: 'Pengguna2' } }, ]), ); const { data: bookData } = useQuery(['buku'], () => Janji.tekad({ nomor identitas: '1', nama: 'Nama 1', penulis: { id: '1001', nama: 'Pengguna1' }, }), ); const updateBookNameMutation = useMutation({ mutasiFn: () => ({ nomor identitas: '1', nama: 'Nama 1 Diperbarui', }),- onSuccess: mutasiData => {- queryClient.setQueryData(['buku'], data =>- data.map(buku =>- buku.id === mutasiData.id ? { ...buku, . ..mutationData } : buku,- ),- );- queryClient.setQueryData(['buku'], data =>- data.id === mutasiData.id ? { ...data, ...mutationData } : data,- );- },}); const updateBookAuthorMutation = useMutation({ mutasiFn: () => ({ nomor identitas: '1', penulis: { id: '1004', nama: 'Pengguna4' }, }),- onSuccess: mutasiData => {- queryClient.setQueryData(['buku'], data =>- data.map(buku =>- buku.id === mutasiData.id ? { ...buku, . ..mutationData } : buku,- ),- );- queryClient.setQueryData(['buku'], data =>- data.id === mutasiData.id ? { ...data, ...mutationData } : data,- );- },}); const addBookMutation = penggunaanMutation({ mutasiFn: () => ({ nomor identitas: '3', nama: 'Nama 3', penulis: { id: '1003', nama: 'Pengguna3' }, }), // dengan data dengan array tingkat atas, Anda masih perlu memperbarui data secara manual onSuccess: mutasiData => { queryClient.setQueryData(['buku'], data => data.concat(mutationData)); }, }); // mengembalikan beberapa BEJ }; const Aplikasi = () => (+ <QueryNormalizerProvider queryClient={queryClient}> <QueryClientProvider client={queryClient}> <Buku /> </QueryClientProvider>+ </QueryNormalizerProvider> );
Jadi, seperti yang Anda lihat, selain array tingkat atas, pembaruan data manual tidak diperlukan lagi. Hal ini sangat berguna jika mutasi tertentu harus memperbarui data untuk beberapa kueri. Tidak hanya perlu melakukan pembaruan secara manual, tetapi Anda juga perlu mengetahui secara pasti kueri mana yang akan diperbarui. Semakin banyak pertanyaan yang Anda miliki, semakin besar keuntungan yang diberikan normy
.
Bagaimana cara kerjanya? Secara default, semua objek dengan kunci id
diatur berdasarkan idnya. Sekarang, objek apa pun dengan id
kunci akan dinormalisasi, yang berarti disimpan oleh id. Jika sudah ada objek yang cocok dengan id yang sama, objek baru akan digabungkan secara mendalam dengan objek yang sudah ada di status. Jadi, jika data respons server dari mutasi adalah { id: '1', title: 'new title' }
, perpustakaan ini akan secara otomatis mencari cara untuk memperbarui title
objek dengan id: '1'
untuk semua kueri dependen.
Ini juga berfungsi dengan objek bersarang dengan id, tidak peduli seberapa dalam. Jika suatu objek dengan id memiliki objek lain dengan id, maka objek tersebut akan dinormalisasi secara terpisah dan objek induk hanya akan merujuk ke objek yang disarangkan tersebut.
Untuk menginstal paket, jalankan saja:
$ npm install @normy/react-query
atau Anda bisa menggunakan CDN saja: https://unpkg.com/@normy/react-query
.
Untuk menginstal paket, jalankan saja:
$ npm install @normy/swr
atau bisa juga menggunakan CDN: https://unpkg.com/@normy/swr
.
Untuk menginstal paket, jalankan saja:
$ npm install @normy/rtk-query
atau Anda bisa menggunakan CDN saja: https://unpkg.com/@normy/rtk-query
.
Jika Anda ingin menulis plugin ke perpustakaan lain selain react-query
, swr
atau rtk-query
:
$ npm install @normy/core
atau Anda bisa menggunakan CDN saja: https://unpkg.com/@normy/core
.
Untuk melihat cara menulis plugin, untuk saat ini cukup cek source code @normy/react-query
, caranya sangat mudah, kedepannya akan dibuatkan panduannya.
Agar normalisasi otomatis berfungsi, kondisi berikut harus dipenuhi:
Anda harus memiliki cara standar untuk mengidentifikasi objek Anda, biasanya ini dilakukan dengan id
kunci
id harus unik di seluruh aplikasi, tidak hanya di seluruh jenis objek, jika tidak, Anda perlu menambahkan sesuatu ke dalamnya, hal yang sama harus dilakukan di dunia GraphQL, biasanya menambahkan _typename
objek dengan id yang sama harus memiliki struktur yang konsisten, jika objek seperti buku dalam satu kueri memiliki kunci title
, maka objek tersebut harus berupa title
di kueri lain, bukan name
yang tiba-tiba
Ada fungsi yang bisa diteruskan ke createQueryNormalizer
untuk memenuhi persyaratan tersebut, yaitu getNormalizationObjectKey
.
getNormalizationObjectKey
dapat membantu Anda dengan poin pertama, jika misalnya Anda mengidentifikasi objek secara berbeda, seperti dengan kunci _id
, maka Anda dapat meneruskan getNormalizationObjectKey: obj => obj._id
.
getNormalizationObjectKey
juga memungkinkan Anda memenuhi persyaratan ke-2. Misalnya, jika id Anda unik, tetapi tidak di seluruh aplikasi, tetapi dalam tipe objek, Anda dapat menggunakan getNormalizationObjectKey: obj => obj.id && obj.type ? obj.id + obj.type : undefined
atau yang serupa. Jika tidak memungkinkan, Anda dapat menghitung sendiri sufiksnya, misalnya:
const getType = obj => { if (obj.bookTitle) {kembalikan 'buku'; } if (obj.nama belakang) {kembalikan 'pengguna'; } kembalikan tidak terdefinisi;};createQueryNormalizer(queryClient, { getNormalizationObjectKey: obj =>obj.id && getType(obj) && obj.id + getType(obj),});
Poin 3 harus selalu dipenuhi, jika tidak, Anda harus meminta pengembang backend Anda untuk menjaga standar dan konsisten. Sebagai upaya terakhir, Anda dapat mengubah tanggapan di pihak Anda.
Sayangnya ini tidak berarti Anda tidak perlu lagi memperbarui data secara manual. Beberapa update masih perlu dilakukan secara manual seperti biasanya, yaitu menambah dan menghapus item dari array. Mengapa? Bayangkan mutasi REMOVE_BOOK
. Buku ini mungkin ada dalam banyak kueri, perpustakaan tidak dapat mengetahui dari kueri mana Anda ingin menghapusnya. Hal yang sama berlaku untuk ADD_BOOK
, perpustakaan tidak dapat mengetahui kueri mana yang harus ditambahkan buku, atau bahkan indeks array mana. Hal yang sama untuk tindakan seperti SORT_BOOKS
. Masalah ini hanya mempengaruhi array tingkat atas. Misalnya, jika Anda memiliki buku dengan beberapa id dan kunci lain seperti likedByUsers
, maka jika Anda mengembalikan buku baru dengan daftar yang diperbarui di likedByUsers
, ini akan berfungsi kembali secara otomatis.
Namun di versi perpustakaan yang akan datang, dengan beberapa petunjuk tambahan, pembaruan di atas juga dapat dilakukan!
Jika Anda tertarik, manipulasi data apa yang sebenarnya dilakukan normy
, Anda dapat menggunakan opsi devLogging
:
<Penyedia QueryNormalizer klien kueri={klien kueri} normalizerConfig={{ devLogging: benar }}> {anak-anak}</QueryNormalizerProvider>
false
secara default, jika disetel ke true
, Anda dapat melihat informasi di konsol, saat kueri disetel atau dihapus.
Perhatikan bahwa ini hanya berfungsi dalam pengembangan, meskipun Anda meneruskan true
, tidak ada pencatatan yang akan dilakukan dalam produksi (padahal tepatnya process.env.NODE_ENV === 'production'
). NODE_ENV
biasanya disetel oleh bundler modul seperti webpack
untuk Anda, jadi mungkin Anda tidak perlu khawatir tentang menyetel sendiri NODE_ENV
.
Seperti biasa, otomatisasi apa pun memerlukan biaya. Di masa depan, beberapa tolok ukur dapat ditambahkan, namun untuk saat ini pengujian manual menunjukkan bahwa kecuali dalam data Anda terdapat puluhan ribu objek yang dinormalisasi, maka overhead seharusnya tidak terlihat. Namun, Anda memiliki beberapa cara fleksibel untuk meningkatkan kinerja:
Anda hanya dapat menormalkan kueri yang memiliki pembaruan data, dan hanya mutasi yang seharusnya memperbarui data - itu saja, Anda hanya dapat menormalkan sebagian data Anda. Periksa dokumentasi integrasi bagaimana melakukannya.
Seperti 1.
, tetapi untuk kueri dan mutasi dengan data yang sangat besar.
Terdapat optimalisasi bawaan, yang memeriksa data dari respons mutasi apakah data tersebut benar-benar berbeda dari data di penyimpanan yang dinormalisasi. Jika sama, kueri dependen tidak akan diperbarui. Jadi, sebaiknya data mutasi hanya menyertakan hal-hal yang sebenarnya berbeda, sehingga dapat mencegah normalisasi dan pembaruan kueri yang tidak perlu.
Jangan nonaktifkan opsi structuralSharing
di perpustakaan yang mendukungnya - jika data kueri setelah pembaruan memiliki referensi yang sama dengan sebelum pembaruan, maka kueri ini tidak akan dinormalisasi. Ini adalah pengoptimalan kinerja yang besar, terutama setelah pengambilan ulang saat pemfokusan ulang, yang dapat memperbarui beberapa kueri secara bersamaan, biasanya ke data yang sama.
Anda dapat menggunakan fungsi getNormalizationObjectKey
untuk mengatur secara global objek mana yang harus dinormalisasi. Misalnya:
<Penyedia QueryNormalizer klien kueri={klien kueri} normalizerConfig={{getNormalizationObjectKey: obj => (obj.normalizable ? obj.id : tidak terdefinisi), }}> {anak-anak}</QueryNormalizerProvider>
Selain itu, di masa depan beberapa opsi tambahan khusus kinerja akan ditambahkan.
Saat ini terdapat tiga integrasi resmi dengan perpustakaan pengambilan data, yaitu react-query
, swr
dan rtk-query
. Lihat dokumentasi khusus untuk integrasi spesifik:
permintaan reaksi
swr
permintaan-rtk
Saya sangat merekomendasikan untuk mencoba contoh bagaimana paket ini dapat digunakan dalam aplikasi nyata.
Ada contoh berikut saat ini:
permintaan reaksi
trpc
swr
permintaan-rtk
MIT