資料擷取庫的自動標準化和資料更新(react-query、swr、rtk-query 等)
介紹
動機
安裝
所需條件
數組標準化
偵錯
表現
整合
範例
normy
是一個庫,它允許您的應用程式資料自動標準化。然後,一旦數據標準化,在許多情況下您的數據就可以自動更新。
normy
的核心 - 即@normy/core
庫,並不意味著直接在應用程式中使用,其內部邏輯允許與您最喜歡的數據獲取庫輕鬆整合。已經有與react-query
、 swr
和RTK Query
官方整合。如果您使用其他獲取庫,則可能會引發 Github 問題,因此也可能會添加它。
為了理解normy
實際上做了什麼,最好看一個例子。假設您使用react-query
。然後你可以透過以下方式重構程式碼:
從“反應”導入反應; 進口 { 查詢客戶端提供者, 查詢客戶端, 使用查詢客戶端, } from '@tanstack/react-query';+ import { QueryNormalizerProvider } from '@normy/react-query'; const queryClient = new QueryClient(); const 書籍 = () => { const queryClient = useQueryClient(); const { data: booksData = [] } = useQuery(['books'], () => Promise.resolve([ { id: '1', name: '姓名1', 作者: { id: '1001', name: '使用者1' } }, { id: '2', name: '姓名 2', 作者: { id: '1002', name: '使用者2' } }, ]), ); const { data: bookData } = useQuery(['book'], () => Promise.resolve({ id: '1', 名稱:'名稱1', 作者: { id: '1001', name: 'User1' }, }), ); const updateBookNameMutation = useMutation({ 突變Fn:()=>({ id: '1', name: '名稱 1 已更新', }),- onSuccess:mutationData => {- queryClient.setQueryData(['books'], data =>- data.map(book =>- book.id ===mutationData.id ? { ...book, . ..mutationData } : book,- ),- );- queryClient.setQueryData(['book'], data =>- data.id ===mutationData.id ? { ...data, ...mutationData } :數據,- );- },}); const updateBookAuthorMutation = useMutation({ 突變Fn:()=>({ id: '1', 作者: { id: '1004', name: 'User4' }, }),- onSuccess:mutationData => {- queryClient.setQueryData(['books'], data =>- data.map(book =>- book.id ===mutationData.id ? { ...book, . ..mutationData } : book,- ),- );- queryClient.setQueryData(['book'], data =>- data.id ===mutationData.id ? { ...data, ...mutationData } :數據,- );- },}); const addBookMutation = useMutation({ 突變Fn:()=>({ id: '3', 名稱:'名稱3', 作者:{ id: '1003', name: 'User3' }, }), // 對於頂級數組的數據,仍然需要手動更新數據 onSuccess: 突變資料 => { queryClient.setQueryData(['books'], data => data.concat(mutationData)); }, }); // 回傳一些 JSX }; const App = () => (+ <QueryNormalizerProvider queryClient={queryClient}> <QueryClientProvider client={queryClient}> <書籍/> </QueryClientProvider>+ </QueryNormalizerProvider> );
因此,正如您所看到的,除了頂級數組之外,不再需要手動資料更新。如果給定的突變應該更新多個查詢的數據,這尤其方便。手動執行更新不僅很冗長,而且您還需要確切地知道要更新哪些查詢。您的查詢越多, normy
帶來的優勢就越大。
它是如何運作的?預設情況下,所有具有id
鍵的物件都按其 id 進行組織。現在,任何具有鍵id
的物件都將被標準化,這簡單地意味著透過 id 儲存。如果已經存在具有相同 id 的匹配對象,則新的對象將與狀態中已有的對象深度合併。因此,如果來自突變的伺服器回應資料是{ id: '1', title: 'new title' }
,則該程式庫將自動計算更新所有相關查詢的id: '1'
物件的title
。
它也適用於帶有 id 的嵌套對象,無論多深。如果一個具有 id 的對像有其他具有 id 的對象,那麼這些對象將被單獨規範化,並且父對象將僅引用這些嵌套對象。
要安裝該軟體包,只需運行:
$ npm install @normy/react-query
或者您可以只使用 CDN: https://unpkg.com/@normy/react-query
://unpkg.com/@normy/react-query 。
要安裝該軟體包,只需運行:
$ npm install @normy/swr
或者您可以只使用 CDN: https://unpkg.com/@normy/swr
。
要安裝該軟體包,只需運行:
$ npm install @normy/rtk-query
或者您可以只使用 CDN: https://unpkg.com/@normy/rtk-query
://unpkg.com/@normy/rtk-query 。
如果你想寫一個外掛到除了react-query
、 swr
或rtk-query
之外的另一個函式庫:
$ npm install @normy/core
或者您可以只使用 CDN: https://unpkg.com/@normy/core
。
要了解如何編寫插件,現在只需查看@normy/react-query
的源代碼,這很容易做到,將來會創建一個指南。
為了使自動歸一化工作,必須滿足以下條件:
你必須有一個標準化的方法來識別你的對象,通常這是透過密鑰id
來完成的
id 在整個應用程式中必須是唯一的,而不僅僅是在物件類型之間,如果不是,您將需要向它們附加一些內容,在 GraphQL 世界中也必須這樣做,通常會添加_typename
具有相同 id 的物件應該具有一致的結構,如果一個查詢中像書這樣的物件具有title
鍵,那麼它在其他查詢中應該是title
,而不是突然出現的name
有一個函數可以傳遞給createQueryNormalizer
來滿足這些要求,即getNormalizationObjectKey
。
getNormalizationObjectKey
可以幫助您解決第一點,例如,如果您以不同的方式標識對象,例如透過_id
鍵,那麼您可以傳遞getNormalizationObjectKey: obj => obj._id
。
getNormalizationObjectKey
還允許您通過第二個要求。例如,如果您的 id 是唯一的,但不是在整個應用程式中唯一,而是在物件類型內,則可以使用getNormalizationObjectKey: obj => obj.id && obj.type ? obj.id + obj.type : undefined
或類似的東西。如果這是不可能的,那麼您可以自己計算後綴,例如:
const getType = obj => { if (obj.bookTitle) {return '書'; } if (obj.surname) {return '使用者'; } 傳回未定義;};createQueryNormalizer(queryClient, { getNormalizationObjectKey: obj =>obj.id && getType(obj) && obj.id + getType(obj),});
第 3 點應該始終得到滿足,如果沒有,您真的應該要求後端開發人員保持標準化和一致性。作為最後的手段,您可以修改自己的回覆。
不幸的是,這並不意味著您永遠不需要再手動更新資料。有些更新仍然需要像平常一樣手動完成,也就是從陣列中新增和刪除項目。為什麼?想像一下REMOVE_BOOK
突變。這本書可能出現在許多查詢中,圖書館無法知道您想從哪些查詢中刪除它。這同樣適用於ADD_BOOK
,圖書館不知道應該將一本書加入哪個查詢,甚至不知道哪個陣列索引。 SORT_BOOKS
之類的操作也是如此。不過,這個問題僅影響頂級數組。例如,如果您有一本書,其中包含某個 id 和另一個鍵(如likedByUsers
),那麼如果您返回帶有更新列表的新書likedByUsers
,這將自動再次起作用。
不過,在該庫的未來版本中,透過一些額外的指示,也可以進行上述更新!
如果您對資料操作normy
實際上做了什麼感興趣,您可以使用devLogging
選項:
<查詢規範化器提供者 查詢客戶端={查詢客戶端} NormalizerConfig={{ devLogging: true }}> {children}</QueryNormalizerProvider>
預設為false
,如果設定為true
,您可以在設定或刪除查詢時在控制台資訊中看到。
請注意,這僅在開發中有效,即使您傳遞true
,在生產中也不會進行任何日誌記錄(當恰好process.env.NODE_ENV === 'production'
時)。 NODE_ENV
通常由webpack
等模組捆綁程式為您設置,因此您可能不需要擔心自己設定NODE_ENV
。
一如既往,任何自動化都會帶來成本。將來可以添加一些基準,但目前手動測試表明,除非您的資料中有數以萬計的標準化對象,否則開銷應該不明顯。但是,您可以透過多種靈活的方法來提高效能:
您可以僅規範化具有資料更新的查詢,以及僅應更新資料的突變 - 就是這樣,您只能規範化部分資料。檢查集成文件如何執行此操作。
與1.
類似,但適用於具有極大數據的查詢和突變。
有一個內建的優化,它檢查突變響應中的數據是否實際上與標準化儲存中的數據不同。如果相同,則相關查詢將不會更新。因此,突變資料最好只包含實際上可能不同的內容,這可以防止不必要的標準化和查詢更新。
不要在支援它的庫中停用structuralSharing
選項 - 如果更新後的查詢資料與更新前的引用相同,則該查詢將不會被規範化。這是一個很大的效能優化,特別是在重新聚焦時重新獲取之後,它可以同時更新多個查詢,通常更新到完全相同的資料。
您可以使用getNormalizationObjectKey
函數全域設定哪些物件應該實際標準化。例如:
<查詢規範化器提供者 查詢客戶端={查詢客戶端} NormalizerConfig={{getNormalizationObjectKey: obj => (obj.normalized ? obj.id : 未定義), }}> {children}</QueryNormalizerProvider>
此外,將來還會添加一些額外的特定於性能的選項。
目前官方整合了三個資料獲取庫,即react-query
、 swr
和rtk-query
。請參閱特定整合的專用文件:
反應查詢
駐波比
RTK查詢
我強烈建議嘗試範例如何在實際應用程式中使用該套件。
目前有以下例子:
反應查詢
特爾普克
駐波比
RTK查詢
麻省理工學院