您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“ReactQuery數據轉換怎么實現”,內容詳細,步驟清晰,細節處理妥當,希望這篇“ReactQuery數據轉換怎么實現”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
我們不得不面對這個問題-大部分的人并沒有使用GraphQL。如果你使用了,那么恭喜你,因為你可以請求到你期望的數據格式。
如果你在使用REST風格的API,你就必須受限于后端返回的數據格式。所以在使用react-query的時候我們應該在什么地方通過什么方式來進行數據轉換呢?
答案只有一個:看情況。
下面列舉出四種進行數據轉換的方式,以及他們的優缺點:
這是我最喜歡的方式,如果你有決定權的話。如果后端返回的數據結構是你所期望的話,那么你就什么都不用做了。但是在很多場景這并不太現實,比如一些公共的REST API,特別是在企業級應用中。如果你可以讓后端針對每一個具體的場景都有一個對應的接口,那么可以返回你期望的數據結構。
優點:
前端什么都不用做
缺點:
并不是所有情況下都能做到
查詢函數是你傳給useQuery
的函數。他會返回一個Promise,最終返回的數據會被存在緩存中。但是這并不意味著你只能按照后端給你的數據結構來返回數據。你可以在返回之前進行數據轉換:
const fetchTodos = async (): Promise<Todos> => { const response = await axios.get('todos') const data: Todos = response.data return data.map((todo) => todo.name.toUpperCase()) } export const useTodosQuery = () => useQuery(['todos'], fetchTodos)
之后你就可以在其他地方使用轉換之后的數據,仿佛后端返回的數據就是這樣的。你在其他地方都不會拿到不是大寫的todo名字了。同時你也拿不到數據的原始結構了。如果你查看react-query-devtools,你會看到轉換之后的結構。如果你查看網絡請求,你可以看到原始的數據結構。這個可能會有點讓人感到困惑,所以不要忘了你在代碼里面處理了數據結構。
同時,在這里react-query并不會做什么優化。也就是說每一次fetch被執行的時候,你的轉換邏輯都會被執行。如果轉換邏輯很復雜,需要考慮一下其他轉換方式。一些公司在前端會有一個公共的API層來抽象數據獲取,所以你可能沒辦法在這個抽象層里面做你的數據轉換。
優點:
和API調用綁定在一起,對上層無感知
缺點:
在每次數據請求的時候都會運行
如果你有一個你無法修改的公共的API層,這個方式不太可行
其他:
存儲在緩存中的是轉換之后的數據結構,所以你沒辦法拿到原始的數據結構
你可以自定義一個hook,那么你可以很方便的在這個hook里做數據轉換:
const fetchTodos = async (): Promise<Todos> => { const response = await axios.get('todos') return response.data } export const useTodosQuery = () => { const queryInfo = useQuery(['todos'], fetchTodos) return { ...queryInfo, data: queryInfo.data?.map((todo) => todo.name.toUpperCase()), } }
正如代碼邏輯所示,數據轉換不會在每次數據查詢的時候運行,但是會在每次render的時候運行(即使這次render并沒有觸發數據請求)。這看起來這不是什么大問題,如果你在意的話,你可以通過useMemo
來進行優化,同時盡可能只定義真正需要的依賴列表。queryInfo中的data
是引用穩定的除非數據真的發生了變化,但是queryInfo
就不是了。如果你把queryInfo
作為你的依賴,那么轉換邏輯就會在每次render的時候運行:
export const useTodosQuery = () => { const queryInfo = useQuery(['todos'], fetchTodos) return { ...queryInfo, // ???? don't do this - the useMemo does nothing at all here! data: React.useMemo( () => queryInfo.data?.map((todo) => todo.name.toUpperCase()), [queryInfo] ), // ? correctly memoizes by queryInfo.data data: React.useMemo( () => queryInfo.data?.map((todo) => todo.name.toUpperCase()), [queryInfo.data] ), } }
特別是當你在自定義hook中有一些額外的邏輯來協助進行數據轉換的時候,這是一個很好的選擇。需要注意的是data有可能是undefined,所以請使用可選鏈式訪問來獲取data中的數據。
優點:
可以通過useMemo進行優化
缺點
寫法有一些晦澀
data可能會是undefined
其他
確切的數據結構無法在devtool中展示
v3引入了內置的selector,可以用它來進行數據轉換:
export const useTodosQuery = () => useQuery(['todos'], fetchTodos, { select: (data) => data.map((todo) => todo.name.toUpperCase()), })
selector只會在data存在的時候被調用,所以你不用擔心undefiend的問題。像上面的selector會在每次render的時候被執行,因為函數表達式變化了(因為這是一個內聯函數)。如果轉換邏輯比較復雜,你可以使用useCallback來進行memoize,或者把他抽象到一個穩定的函數引用中:
const transformTodoNames = (data: Todos) => data.map((todo) => todo.name.toUpperCase()) export const useTodosQuery = () => useQuery(['todos'], fetchTodos, { // ? uses a stable function reference select: transformTodoNames, }) export const useTodosQuery = () => useQuery(['todos'], fetchTodos, { // ? memoizes with useCallback select: React.useCallback( (data: Todos) => data.map((todo) => todo.name.toUpperCase()), [] ), })
在未來,select配置也可以被用來訂閱data中的部分數據。這使得這一數據轉換實現方式變得特別。看看下面這個例子:
export const useTodosQuery = (select) => useQuery(['todos'], fetchTodos, { select }) export const useTodosCount = () => useTodosQuery((data) => data.length) export const useTodo = (id) => useTodosQuery((data) => data.find((todo) => todo.id === id))
這里,我們創建了一個像useSelector一樣的API,你可以傳自定義selector到useTodosQuery中。這個自定義hook仍然可以像之前一樣工作,如果你沒有傳select,會返回整個數據。
但是如果你傳了selector,你就只會訂閱selector返回的部分數據。這是很有用的,因為這意味著如果我們更新了一個todo的名字,只通過useTodosCount訂閱了count的組件并不會重新渲染。count沒有發生變化,所以react-query可以選擇不通知這部分數據的訂閱者(注意這里說得很容易,但是具體實現不完全跟這個描述一樣,我會在第三部分渲染優化中聊一聊這部分內容)
優點:
最佳優化
支持部分訂閱
其他:
每個訂閱者的數據可能都不一樣
讀到這里,這篇“ReactQuery數據轉換怎么實現”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。