React Query 是做什么的?你知道嗎?
最近因?yàn)楣ぷ鞯年P(guān)系,開始接觸 React Query[1]。不用不知道,一用真好用!
React Query 是以前的叫法,現(xiàn)在叫 TanStack Query。之所以改名字,是因?yàn)檫@個(gè)團(tuán)隊(duì)發(fā)現(xiàn),他們可以把這套方案推廣到除 React 之外的其他框架中去。
圖片
如圖所示,目前(2024.06)最新的 v5 版本已經(jīng)支持包含 React、Vue、Angular 在內(nèi)的 5 大框架了。
那 React Query 到底是做什么的呢?
籠統(tǒng)地說,React Query 是用來管理接口請求的,包括增刪改查所有類型的接口。管理的內(nèi)容包括響應(yīng)數(shù)據(jù)和請求狀態(tài),可以讓你少些很多樣板代碼。
另外,一旦學(xué)會(huì)了 React Query 的使用,那么在其他框架中的應(yīng)用也是一樣,上手就快了。
不過 React Query 學(xué)習(xí)成本也高,要徹底熟練 React Query 的使用,就要學(xué)習(xí)很多概念,不過理解這些概念對于我們寫出交互友好的頁面又極其關(guān)鍵。
于是,我便萌生了寫一個(gè) React Query 系列文章的想法。本文就是第一篇,大概談?wù)撍亲鍪裁吹模绾问褂?,有什么能力,后面再一篇一個(gè)具體話題單獨(dú)討論。
React Query 是從 v3 版本改名字的:
- v3 以前(包括 v3)包名叫 react-query
- 從 v4 開始包名改成 @tanstack/react-query 了
新舊版本改動(dòng)不多[2],因?yàn)槲椰F(xiàn)在用的是舊包,我就那它舉例了。
安裝 React Query
先創(chuàng)建一個(gè) React 項(xiàng)目。
npm create vite@latest react-query-demos -- --template react
cd react-query-demos
安裝 react-query[3] 依賴,啟動(dòng)項(xiàng)目。
npm install react-query
npm install
npm run dev
接下來刪除 index.css 中的內(nèi)容,再修改 App.jsx,注入 React Query 上下文依賴。
import { QueryClient, QueryClientProvider, useQuery } from 'react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
{/* ... */}
</QueryClientProvider>
)
}
這一步是必須的,后續(xù) React Query 的接口查詢和修改等 API 能力都有賴于 queryClient。
快速開始
說了那么多,我們直接上一個(gè) React Query 案例直觀感受一下它的使用。
我們創(chuàng)建一個(gè) <Example /> 組件,內(nèi)容如下:
import { useQuery } from 'react-query'
function Example() {
// 1)
const { isLoading, isError, error, data } = useQuery('repoData', () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res =>
res.json()
)
)
// 2)
if (isLoading) return 'Loading...'
if (isError) return 'An error has occurred: ' + error.message
// 3)
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>?? {data.subscribers_count}</strong>{' '}
<strong>? {data.stargazers_count}</strong>{' '}
<strong>?? {data.forks_count}</strong>
</div>
)
}
速覽一遍代碼,我們大概就能明白這塊代碼是用來做數(shù)據(jù)請求的。
再在 <App /> 中引入。
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
我這里簡單解釋一下 <Example /> 中的內(nèi)容:
- React Query 有暴露出一個(gè) useQuery 這個(gè) React Hook 用來進(jìn)行數(shù)據(jù)查詢
- 值得注意的是,React Query 本身并不提供數(shù)據(jù)請求能力,useQuery 的第二個(gè)參數(shù)中返回使用 Fetch API 請求的 Github 倉庫數(shù)據(jù)(Promise 數(shù)據(jù))
- useQuery 的第一個(gè)參數(shù),則是用來定義請求數(shù)據(jù)的緩存 key,這個(gè) key 在同一個(gè) queryClient 下唯一。這樣在進(jìn)行第二次請求時(shí),會(huì)先直接返回先前的緩存數(shù)據(jù),提升用戶體驗(yàn)
- useQuery() 返回值非常貼心的為你封裝了請求狀態(tài),常用的有 isLoading、isError
isLoading 為 true 表示目前處于請求中
isError 表示請求失敗,失敗消息可以通過 error 獲得
當(dāng)然還有一個(gè) isSuccess,不過不太常用。一般在判斷完 isLoading 和 isError 之后,就說明我們已經(jīng)成功獲得了數(shù)據(jù),通過 data 渲染結(jié)果即可
圖片
三個(gè)核心概念
React Query 中有 3 個(gè)核心的概念[4],分別是:
- 查詢(Queries)
- 修改(Mutations)
- 作廢緩存(Query Invalidation)
對應(yīng) 3 個(gè) API:
- useQuery
- useMutation
- queryClient.invalidateQueries()
上面一節(jié),我們就講解了“查詢”的使用。
以上我們講解了 React Query 所提供的數(shù)據(jù)能力。
接下來,我們再舉一個(gè)完成的例子,說明所有 3 個(gè)核心概念。
首先,封裝 API 請求,我們把它放在 my-api.js 文件中。
export function getTodos() {
return fetch('https://jsonplaceholder.typicode.com/users/1/todos')
.then(response => response.json())
}
export function postTodo(data) {
return fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
})
.then((response) => response.json())
}
借用了 {JSON} Placeholder[5] 的服務(wù),我們封裝了獲取和更新代辦項(xiàng)目的方法:getTodos() 和 postTodo()。
接著,我們寫一個(gè) <Todos> 組件,替換之前的 <Example /> 組件。
import {
useQuery,
} from 'react-query'
function Todos() {
// Queries
const query = useQuery('todos', getTodos)
if (query.isLoading) return 'Loading...'
if (query.isError) return 'An error has occurred: ' + query.error.message
return (
<div>
<ul>
{query.data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
)
}
與之前的 <Example> 大同小異,這里渲染的是一個(gè)列表。
不過我們還會(huì)增加一個(gè)添加代辦項(xiàng)的功能。
import {
useQuery,
useMutation
} from 'react-query'
function Todos() {
// Queries
const query = useQuery('todos', getTodos)
// 1)
// Mutations
const mutation = useMutation(postTodo)
if (query.isLoading) return 'Loading...'
if (query.isError) return 'An error has occurred: ' + query.error.message
return (
<div>
<ul>
{query.data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
{/* 2) */}
<button
disabled={mutation.isLoading}
onClick={() => {
mutation.mutate({
userId: 1,
title: 'Do Laundry',
completed: false
})
}}
>
Add Todo
</button>
</div>
)
}
- 首先,引入一個(gè) useMutation() Hook,包裝添加代辦項(xiàng)的接口能力
- useMutation() 的返回值 mutation 跟 useQuery() 的返回值 query 一樣,也是一個(gè)復(fù)合對象
你可以通過 mutation.mutate() 進(jìn)行實(shí)際操作(本例中就是添加代辦)
也可以通過 mutation.isLoading/isError 等獲得請求狀態(tài)
最后一個(gè)功能就是作廢緩存。
function Todos() {
// 1)
// Access the client
const queryClient = useQueryClient()
// Mutations
const mutation = useMutation(postTodo, {
onSuccess: () => {
// 2)
// Invalidate and refetch
queryClient.invalidateQueries('todos')
},
})
// ...
}
- 這里就要通過 useQueryClient() 引入 queryClient 了。
- 作廢緩存的時(shí)機(jī)是在添加完代辦后,因?yàn)檫@個(gè)時(shí)候代辦列表變化了,我們調(diào)用 queryClient.invalidateQueries('todos') 方法,代入的 'todos' 其實(shí)就是查詢代辦項(xiàng)時(shí)使用那個(gè)緩存 key。
調(diào)用 .invalidateQueries('todos') 就表示通過 GET '/users/1/todos' 拿到的數(shù)據(jù)已經(jīng)過期了,要重新請求。
所以你會(huì)看到點(diǎn)擊“Add Todo”按鈕后,控制臺(tái)還會(huì)多發(fā)出一個(gè) GET '/users/1/todos' 請求
圖片
總結(jié)
React Query 本質(zhì)上幫你管理接口請求的,管理的內(nèi)容包括響應(yīng)數(shù)據(jù)和請求狀態(tài),可以讓你少些很多樣板代碼。
值得注意的是,React Query 本身并不提供接口請求能力,你可以通過 Fetch API 或是 axios 這種三方庫提供。
React Query 中有 3 個(gè)核心的概念,分別是:
- 查詢(Queries)
- 修改(Mutations),和
- 作廢緩存(Query Invalidation)
對應(yīng) 3 個(gè) API:
- useQuery
- useMutation,和
- queryClient.invalidateQueries()
文中也都做了簡單案例介紹,不過每個(gè)概念背后又有跟多其他內(nèi)容,這就要在后續(xù)的文章中覆蓋了。
感謝你的閱讀,希望對你有所幫助,再見。
參考資料
[1]
React Query: https://tanstack.com/query/v3/docs/framework/react/overview
[2]新舊版本改動(dòng)不多: https://tanstack.com/query/v4/docs/framework/react/guides/migrating-to-react-query-4
[3]安裝 react-query: https://tanstack.com/query/v3/docs/framework/react/installation
[4]React Query 中有 3 個(gè)核心的概念: https://tanstack.com/query/v3/docs/framework/react/quick-start
[5]{JSON} Placeholder: https://jsonplaceholder.typicode.com/