在 React 開發中,我們經常會從伺服器端取得資料,通常會使用 useEffect
搭配 useState
來 fetch 資料。然而,這種方式雖然可以解決問題,但有可能會遇到以下問題:
還有其他許多可能遇到的問題,可以想像如果這些問題都要處理,需要寫很多的程式碼,導致邏輯很複雜。
這時候使用 Server State Management 的套件就能有效解決以上問題。不只是單純處理 fetch 和 state 管理,這些 library 通常還能提供快取、資料同步、背景更新等功能。
細節的比較可以參考 這裡。基本上概念都是類似的,有部份功能上細節的差異,主要介紹會是以 React Query 為主。
npm install @tanstack/react-query
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
export function App({ children }) {
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
}
import { useQuery } from "@tanstack/react-query";
import { fetchUserData } from "./api";
export default function UserList() {
const { data, error, isLoading } = useQuery({
queryKey: ["users"],
queryFn: fetchUserData,
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>User Data</h1>
<div>
{data.map((user) => (
<div key={user.id}>
<p>Name: {user.name}</p>
</div>
))}
</div>
</div>
);
}
queryKey
: 會是一個陣列,可以用 sting
或是複雜結構的 array
,只要是可序列化並且是唯一的,用來管理快取。queryFn
: 可以是返回 Promise 的任何函數,用來定義如何獲取數據
主要架構會長這樣,除此之外 useQuery
也有其他選項來設定:
refetchOnWindowFocus
: 設定當瀏覽器視窗重新取得焦點時,是否自動重新抓取資料,預設為 true
。refetchInterval
: 設定每隔幾秒重新獲取資料。enable
: 設定是否啟用這次資料請求,在某些條件滿足時才發送 API 請求。gcTime
(之前叫 cacheTime) : 新鮮資料的維持時間,過期後的任何新請求,都會重新獲取,預設 0 秒。staleTime
: 設定快取資料在不再被使用後,多久會被自動刪除,通常會設置得比 staleTime 大,預設 5 分鐘。除了在 useQuery
設定,也可以在 QueryClient
設定 defaultOptions
。
這些設定將應用於所有 useQuery
,除非在單獨的 useQuery
中覆蓋這些值。
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5000,
gcTime: 300000,
refetchOnWindowFocus: false,
//...
},
},
});
import { addUser } from "./api";
import { useMutation, useQueryClient } from "@tanstack/react-query";
export default function AddUser() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: addUser,
//當 mutation 成功後執行
onSuccess: () => {
//告知 React Query queryKey 無效並重新取得資料
queryClient.invalidateQueries({ queryKey: ["users"] });
},
});
return (
<button onClick={() => mutation.mutate({ id: Date.now(), name: "John" })}>
Add User
</button>
);
}
參考資料:
https://tkdodo.eu/blog/why-you-want-react-query
https://www.youtube.com/watch?v=OrliU0e09io
https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect
https://tanstack.com/query/latest/docs/framework/react/overview
https://tanstack.com/query/latest/docs/framework/react/guides/caching#basic-example