快取狀態與除錯工具
寫在前面
由於國內較少有比較系統的react-query教程,因此筆者結合官方文件以及官方課程的內容,希望寫一個較為全面的教程。本文將以各種例子作為切入點,儘可能通俗易懂地講解相關知識點。如果有錯誤,還請大家在評論區指出,筆者會盡快改正。
目錄
- 入門react-query 已於2022-06-04更新
- 深入查詢鍵及查詢函式 已於2022-06-08更新
- 並行請求及依賴請求 已於2022-06-19更新
- 查詢結果快取狀態與除錯工具 已於2022-06-23更新
由於快取狀態處於幕後,難以直觀地展現相關的變化,因此本章同時也會介紹除錯工具的相關使用,來詳細地說明,並且直觀地展現相關狀態的變化。
快取狀態
react-query通常在掛載元件時獲取資料;在獲取資料後,將資料儲存到快取中,並將該資料提供給元件使用。
在react-query獲取資料的過程中,主要會經歷以下三種狀態:
loading
error
success
這三種狀態(status
)的流程關係如下:
graph TD
loading --> success
loading --> error
對於有開發經驗的同學應該不難理解,請求介面後,要麼成功,要麼失敗。
上面的三種狀態其實也對應useQuery
鉤子中的isLoading
、isSuccess
、isError
屬性。也可以通過status
屬性,獲取到loading
、success
、error
。
當react-query進行後端請求查詢時,會有以下三個狀態:
idle
:空閒,表示當前不需要從後端獲取資料fetching
: 獲取資料,表示當前正在從後端獲取資料paused
:暫停,表示原本嘗試從後端獲取資料,但是通常由於未聯網的原因導致暫停
fetchStatus
將會在idle
、fetching
、paused
這三個狀態間經歷迴圈:
graph TD
idle --> fetching
fetching --> paused
paused --> fetching
fetching --> idle
?注意
在react-query中
status
為loading
狀態(或者isLoading
為true
)指的是第一次從後端獲取成功之前的狀態,你可以檢視這個線上例子來理解這個狀態持續的週期:檢視線上例子而
fetchStatus
為fetching
狀態(或者isFetching
為true
)指的是每次從後端獲取資料的載入狀態(包含第一次獲取資料)。
整個生命週期,舉個例子:
假如你使用react-query從Github的介面請求了react的issue列表,你此次的請求結果將會在status
中標記為success
或error
,或者從isSuccess
、isError
中判斷請求成功或者失敗。
請求後端資料成功後,在寫入快取時,此時的快取狀態是fresh(最新)
狀態,但是很快(預設過期時間是0ms
)就會變為stale(老舊)
狀態。
如果使用react的issue列表的每個元件都被解除安裝後,issue列表資料的快取狀態將會被標記為inactive(不活躍)
狀態。此時資料將不會被刪除,直到一段時間後(預設為5分鐘),react-query將會從快取中刪除該條資料。
在變為inactive(不活躍)
狀態之前,該條資料將會在fresh(最新)
與stale(老舊)
之間來回切換,同時介面請求狀態也會在idle
與fetching
之間切換。
??下面將詳細描述該例子過程的細節
使用DevTools來觀察快取狀態變化
為了更加直觀地觀察快取狀態的變化,可以使用DevTools進行直觀地展示。你需要在你的入口檔案中,新增下面的程式碼
import * as React from 'react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
+ import { ReactQueryDevtools } from 'react-query/devtools';
import App from './App';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
const queryClient = new QueryClient();
root.render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
+ <ReactQueryDevtools initialIsOpen />
</QueryClientProvider>
</StrictMode>
);
上面的示例程式碼中,我們引入並設定了ReactQueryDevtools
元件的initialIsOpen
屬性,因此將會預設開啟除錯工具
接著我們開始請求介面,你可以在除錯工具中看到當前所有快取狀態的概覽,如下圖所示:
你也可以開啟線上演示觀察,在例子中點選【重新整理】按鈕,來觀察快取狀態的變化。
fresh(最新)
資料及stale(老舊)
資料
react-query是否會觸發查詢函式,並從後端介面獲取資料,與快取狀態是:fresh(最新)
狀態或stale(老舊)
狀態有關。如果快取狀態是stale(老舊)
,表示該查詢將會有資格重新獲取,但如果快取狀態是fresh(最新)
的,就不會重新獲取。
注意?
如果你想更好地控制介面請求的頻率,請務必記住這些內容。
在預設情況下,後端返回的資料其快取狀態將會立即({staleTime: 0}
)從fresh(最新)
狀態變為stale(老舊)
狀態。
其實這樣做不難理解,因為當你請求到資料後,後端的資料有可能就發生了變化,因此當拿到後端返回的資料瞬間,快取狀態就是stale(陳舊)
的了。
你可以將配置中的staleTime
,設定一個毫秒數的數字,那麼快取將會在staleTime
毫秒後過期(從fresh(最新)
變為stale(陳舊)
)
注意:此時staleTime
不能為0,不然沒有任何意義。
在下面的例子中,將staleTime
設定為了60s,可以在DevTools中觀察一下快取狀態:
import * as React from 'react';
import { useQuery } from 'react-query';
import './style.css';
export default function App() {
const getRepos = (username) =>
fetch(`https://api.github.com/users/${username}/repos`).then((res) =>
res.json()
);
const reposQuery = useQuery(
['repos', 'facebook'],
() => getRepos('facebook'),
+ { staleTime: 1000 * 60 }
);
return (
<div>
<button
onClick={() => {
reposQuery.refetch();
}}
>
重新整理
</button>
<p>
{reposQuery.isLoading && '首次載入中...'}
{reposQuery.isFetching && '重新整理中...'}
</p>
<p>{JSON.stringify(reposQuery.data)}</p>
</div>
);
}
如果把staleTime
設定為Infinity
,表示當前查詢的資料將只會獲取一次,且會在整個網頁的生命週期內快取。
現在應該很清楚地瞭解了,快取狀態是如何從fresh(最新)
變為stale(老舊)
的過程了。那麼是資料一旦變為stale(老舊)
就會重新獲取麼?並不是,而是需要滿足一定的觸發條件才可以。下面將介紹這些觸發條件:
什麼時候會觸發重新獲取資料操作?
在react-query中並不是快取從fresh(最新)
轉換為stale(老舊)
狀態時,就會重新獲取。而是依賴於以下五個觸發條件重新獲取資料資料:
①元件掛載時
當元件首次載入,將會觸發資料的獲取。如果元件被解除安裝後再次被載入,此時也會觸發資料的重新獲取。
②查詢鍵改變時
在前面章節的示例中也提到過,當查詢鍵改變時,將會自動觸發資料的重新獲取。
ps: 如果你的查詢鍵中有物件,也不要擔心react-query無法檢測出來變化,因為react-query會進行深度比對。
③頁面重新被聚焦
當使用者把瀏覽器重新聚焦(比如在瀏覽器中開啟了要除錯的頁面,之後切換到了vscode裡面編輯程式碼,編輯完成後需要看下除錯的頁面效果,又切換到了瀏覽器),或者切換標籤頁(比如開了兩個瀏覽器的選項卡,一個是百度,一個是你的頁面,從百度的選項卡切換至你的頁面)時,react-query都會自動重新獲取資料。
這個觸發條件是預設開啟的,如果希望關閉這個觸發條件,可以把refetchOnWindowFocus
選項設定為false
來禁止。
④網路重新連線
在當前使用者斷網重新聯網後,react-query將會重新獲取資料。比如你的使用者拿著手機在地下通道中,訊號中斷導致斷網,在使用者從地下通道出來時,訊號連線上後,又重新聯網,此時資料將會重新獲取。
這個觸發條件同樣是預設開啟的。如果希望關閉這個觸發條件,可以把refetchOnReconnect
選項設定為false
來禁止。
⑤定時重新整理
這是一個需要你自己配置的一個觸發條件。當你在配置中設定refetchInterval
為數字(代表xxx毫秒)時。無論此時資料是fresh(最新)
還是stale(老舊)
的快取狀態,react-query都會在你設定的毫秒時間間隔內重新獲取資料
以上五種就是react-query內建的重新獲取觸發條件。
注意❗️❗️❗️❗️?
除了定時重新整理外,其它的觸發器,都需要狀態是
stale(陳舊)
才可以觸發,你可以在之前的例子中,嘗試先失焦頁面後,再聚焦(也就是③的觸發條件),你會發現當資料在fresh(最新)
狀態時,並沒有重新請求資料。
清理快取
在快取狀態處於inactive(不活躍)
狀態或者使用這個查詢資料的元件解除安裝時,超過5分鐘(預設情況下)後,react-query將會自動清除該快取。
如果你希望自定義這個時間,你可以使用cacheTime
配置,下面的例子中將cacheTime
設定為0,實現的效果是,當查詢資料在inactive
狀態時,立即從快取中刪除。
const userQuery = useQuery(
["user", username],
() =>
fetch(`https://api.github.com/users/${username}`)
.then(res => res.json()),
{ cacheTime: 0 }
);
當在快取中刪除查詢資料後,此時的表現和這個查詢資料從未載入過一樣,在查詢資料從後端返回前,將看到載入狀態,獲取到資料後,將看到查詢資料。