溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ReactQuery數(shù)據(jù)轉(zhuǎn)換怎么實現(xiàn)

發(fā)布時間:2022-11-11 09:32:52 來源:億速云 閱讀:134 作者:iii 欄目:開發(fā)技術(shù)

本文小編為大家詳細介紹“ReactQuery數(shù)據(jù)轉(zhuǎn)換怎么實現(xiàn)”,內(nèi)容詳細,步驟清晰,細節(jié)處理妥當(dāng),希望這篇“ReactQuery數(shù)據(jù)轉(zhuǎn)換怎么實現(xiàn)”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。

數(shù)據(jù)轉(zhuǎn)換

我們不得不面對這個問題-大部分的人并沒有使用GraphQL。如果你使用了,那么恭喜你,因為你可以請求到你期望的數(shù)據(jù)格式。

如果你在使用REST風(fēng)格的API,你就必須受限于后端返回的數(shù)據(jù)格式。所以在使用react-query的時候我們應(yīng)該在什么地方通過什么方式來進行數(shù)據(jù)轉(zhuǎn)換呢?

答案只有一個:看情況。

下面列舉出四種進行數(shù)據(jù)轉(zhuǎn)換的方式,以及他們的優(yōu)缺點:

后端

這是我最喜歡的方式,如果你有決定權(quán)的話。如果后端返回的數(shù)據(jù)結(jié)構(gòu)是你所期望的話,那么你就什么都不用做了。但是在很多場景這并不太現(xiàn)實,比如一些公共的REST API,特別是在企業(yè)級應(yīng)用中。如果你可以讓后端針對每一個具體的場景都有一個對應(yīng)的接口,那么可以返回你期望的數(shù)據(jù)結(jié)構(gòu)。

  • 優(yōu)點:
    前端什么都不用做

  • 缺點:
    并不是所有情況下都能做到

查詢函數(shù)中

查詢函數(shù)是你傳給useQuery的函數(shù)。他會返回一個Promise,最終返回的數(shù)據(jù)會被存在緩存中。但是這并不意味著你只能按照后端給你的數(shù)據(jù)結(jié)構(gòu)來返回數(shù)據(jù)。你可以在返回之前進行數(shù)據(jù)轉(zhuǎn)換:

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)

之后你就可以在其他地方使用轉(zhuǎn)換之后的數(shù)據(jù),仿佛后端返回的數(shù)據(jù)就是這樣的。你在其他地方都不會拿到不是大寫的todo名字了。同時你也拿不到數(shù)據(jù)的原始結(jié)構(gòu)了。如果你查看react-query-devtools,你會看到轉(zhuǎn)換之后的結(jié)構(gòu)。如果你查看網(wǎng)絡(luò)請求,你可以看到原始的數(shù)據(jù)結(jié)構(gòu)。這個可能會有點讓人感到困惑,所以不要忘了你在代碼里面處理了數(shù)據(jù)結(jié)構(gòu)。

同時,在這里react-query并不會做什么優(yōu)化。也就是說每一次fetch被執(zhí)行的時候,你的轉(zhuǎn)換邏輯都會被執(zhí)行。如果轉(zhuǎn)換邏輯很復(fù)雜,需要考慮一下其他轉(zhuǎn)換方式。一些公司在前端會有一個公共的API層來抽象數(shù)據(jù)獲取,所以你可能沒辦法在這個抽象層里面做你的數(shù)據(jù)轉(zhuǎn)換。

  • 優(yōu)點:
    和API調(diào)用綁定在一起,對上層無感知

  • 缺點:
    在每次數(shù)據(jù)請求的時候都會運行
    如果你有一個你無法修改的公共的API層,這個方式不太可行

  • 其他:
    存儲在緩存中的是轉(zhuǎn)換之后的數(shù)據(jù)結(jié)構(gòu),所以你沒辦法拿到原始的數(shù)據(jù)結(jié)構(gòu)

render函數(shù)中

你可以自定義一個hook,那么你可以很方便的在這個hook里做數(shù)據(jù)轉(zhuǎn)換:

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()),
  }
}

正如代碼邏輯所示,數(shù)據(jù)轉(zhuǎn)換不會在每次數(shù)據(jù)查詢的時候運行,但是會在每次render的時候運行(即使這次render并沒有觸發(fā)數(shù)據(jù)請求)。這看起來這不是什么大問題,如果你在意的話,你可以通過useMemo來進行優(yōu)化,同時盡可能只定義真正需要的依賴列表。queryInfo中的data是引用穩(wěn)定的除非數(shù)據(jù)真的發(fā)生了變化,但是queryInfo就不是了。如果你把queryInfo作為你的依賴,那么轉(zhuǎn)換邏輯就會在每次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]
    ),
  }
}

特別是當(dāng)你在自定義hook中有一些額外的邏輯來協(xié)助進行數(shù)據(jù)轉(zhuǎn)換的時候,這是一個很好的選擇。需要注意的是data有可能是undefined,所以請使用可選鏈?zhǔn)皆L問來獲取data中的數(shù)據(jù)。

  • 優(yōu)點:
    可以通過useMemo進行優(yōu)化

  • 缺點
    寫法有一些晦澀
    data可能會是undefined

  • 其他
    確切的數(shù)據(jù)結(jié)構(gòu)無法在devtool中展示

使用select配置

v3引入了內(nèi)置的selector,可以用它來進行數(shù)據(jù)轉(zhuǎn)換:

export const useTodosQuery = () =>
  useQuery(['todos'], fetchTodos, {
    select: (data) => data.map((todo) => todo.name.toUpperCase()),
  })

selector只會在data存在的時候被調(diào)用,所以你不用擔(dān)心undefiend的問題。像上面的selector會在每次render的時候被執(zhí)行,因為函數(shù)表達式變化了(因為這是一個內(nèi)聯(lián)函數(shù))。如果轉(zhuǎn)換邏輯比較復(fù)雜,你可以使用useCallback來進行memoize,或者把他抽象到一個穩(wěn)定的函數(shù)引用中:

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中的部分數(shù)據(jù)。這使得這一數(shù)據(jù)轉(zhuǎn)換實現(xiàn)方式變得特別??纯聪旅孢@個例子:

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))

這里,我們創(chuàng)建了一個像useSelector一樣的API,你可以傳自定義selector到useTodosQuery中。這個自定義hook仍然可以像之前一樣工作,如果你沒有傳select,會返回整個數(shù)據(jù)。
但是如果你傳了selector,你就只會訂閱selector返回的部分數(shù)據(jù)。這是很有用的,因為這意味著如果我們更新了一個todo的名字,只通過useTodosCount訂閱了count的組件并不會重新渲染。count沒有發(fā)生變化,所以react-query可以選擇不通知這部分數(shù)據(jù)的訂閱者(注意這里說得很容易,但是具體實現(xiàn)不完全跟這個描述一樣,我會在第三部分渲染優(yōu)化中聊一聊這部分內(nèi)容)

  • 優(yōu)點:
    最佳優(yōu)化
    支持部分訂閱

  • 其他:
    每個訂閱者的數(shù)據(jù)可能都不一樣

讀到這里,這篇“ReactQuery數(shù)據(jù)轉(zhuǎn)換怎么實現(xiàn)”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI