資料擷取庫的自動標準化和資料更新(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查詢
麻省理工學院