跳至主要内容

課程:RTK Query 資料管理 第 1 堂:RTK Query 概念建立

31架構與定位

既然我們已經知道傳統的 useEffect + fetch 模式在處理非同步資料時會遇到重複請求、快取缺失等種種痛點,你可能會問:「我已經學過 Redux Toolkit(RTK)了,為什麼不能直接用 createSlice 搭配 createAsyncThunk 來解決這些問題?為什麼 Redux 官方要特別推出 RTK Query 這個工具?」

答案在於:並不是所有的「狀態」都是平等的。

在進入程式碼實作之前,我們必須先釐清 RTK Query 在整個應用程式中的定位,以及它與你已經熟悉的 createSlice 究竟有什麼不同。這將幫助你建立正確的心智模型,避免在未來的專案中過度設計或放錯了資料的位置。


本地客戶端狀態 vs. 遠端伺服器狀態

在現代前端開發中,我們管理的資料可以被分為兩大陣營。理解這兩者的差異,是學好 RTK Query 的第一步。

1. 本地客戶端狀態 (Local Client State)

這是由你的應用程式 完全擁有 並控制的狀態。

  • 來源:使用者操作或 UI 邏輯。
  • 特性:它是同步的,且你是唯一的「事實來源(Source of Truth)」。
  • 範例:深色模式的切換開關、側邊欄是否開啟、目前正在填寫但尚未送出的表單內容、分頁組件的當前頁碼。
  • 管理工具:這正是 createSlice (RTK) 或 useState (React) 大顯身手的地方。

2. 遠端伺服器狀態 (Remote Server State)

這是儲存在伺服器端,你的應用程式 只是暫時借用 並顯示的狀態。

  • 來源:外部 API、資料庫。
  • 特性:它是非同步的。當你顯示這份資料時,伺服器上的原始資料可能已經被其他使用者修改了(這就是為什麼需要快取與失效機制)。
  • 範例:使用者的個人資料、部落格文章列表、電商網站的庫存數量。
  • 管理工具:這就是 RTK Query 專門解決的領域。

對比表格:為什麼我們需要兩套工具?

特性本地狀態 (createSlice)遠端狀態 (RTK Query)
主導權前端應用程式完全控制伺服器控制,前端僅是快取複本
生命週期隨應用程式或組件啟動而產生需要透過網路請求獲取,有載入中/錯誤狀態
資料同步始終是最新(因為是你改的)可能會過時 (Stale),需要定期刷新
主要挑戰狀態邏輯的複雜度、效能優化網路延遲、快取管理、請求去重複

兩者並存的場景:以「商品搜尋頁面」為例

想像你在開發一個電商平台。使用者輸入關鍵字,點擊搜尋,然後看到一列商品。

  • 搜尋關鍵字 (**searchTerm**):這應該存在 createSlice 管理的本地狀態中。因為這是使用者當下的輸入行為,不需要等 API 回傳。
  • 搜尋結果列表 (**products**):這應該交給 RTK Query。當 searchTerm 改變時,RTK Query 會去抓取資料,並幫你處理「載入中」的轉圈圈、快取之前的搜尋結果、以及處理如果 API 壞掉時的錯誤訊息。

思考一下:如果把「搜尋結果」也塞進 createSlice,你得手寫 isLoadingerror 變數,還得處理「如果使用者短時間內點了兩次搜尋,要怎麼取消前一次請求」的麻煩事。而這些,RTK Query 通通內建了。


RTK Query 的四大核心組成 (createApi)

當我們使用 RTK Query 時,所有的核心設定都會集中在一個叫做 createApi 的函式中。你可以把它想像成是在定義一張「API 地圖」。

這張地圖主要由四個部分組成:

1. reducerPath:命名空間

這是一個字串,用來定義這組 API 狀態在 Redux Store 中的「位置」。 就像你在 configureStore 裡定義不同的 slice 一樣,reducerPath 確保了 RTK Query 的快取資料不會跟你的本地狀態混在一起。

  • 語意:它是這份 API 快取在 Store 樹狀結構中的根節點名稱。

2. baseQuery:請求基底

這是所有請求的基礎設定。絕大多數情況下,我們會使用 RTK Query 內建的 fetchBaseQuery

  • 職責:設定 API 的根路徑 (baseUrl)、統一處理 Header(例如注入 Authorization Token)、以及處理基本的請求與回應攔截。
  • 比喻:它就像是公司的「收發室」,所有出去的信件都要先經過這裡蓋章。

3. endpoints:端點定義

這是你定義具體 API 行為的地方。每一個 endpoint 就像是地圖上的一個目的地。

  • 兩種類型
    • query:用於獲取資料 (GET)。
  • mutation:用於修改資料 (POST, PUT, DELETE)。
  • 語意:在這裡你定義了「請求的 URL 是什麼」、「需要帶什麼參數」、「回傳的資料型別是什麼」。

4. 自動生成的 Hooks

這是 RTK Query 最讓開發者驚艷的地方。 只要你在 endpoints 裡定義了一個叫 getPosts 的 query,RTK Query 就會自動生成一個對應的 React Hook,叫做 useGetPostsQuery

  • 價值:你不需要手動 dispatch action,不需要寫 useEffect。直接在組件裡呼叫這個 Hook,它就會自動幫你發請求、回傳資料、並提供 isLoading 等狀態。

理解「RTK Query 實質上是一個特別的 Slice」

對於已經熟悉 createSlice 的你來說,切換到 RTK Query 可能會覺得這是一個完全不同的外星科技。但其實,RTK Query 的底層依然是 Redux。

當你呼叫 createApi 時,它在背後幫你做了以下幾件事:

  1. 自動生成一個 Slice:它幫你定義好了存放 API 回應、載入狀態、錯誤訊息的 state 結構。
  2. 自動生成 Reducers:當網路請求開始、成功或失敗時,它有一套內建的邏輯來更新 state。
  3. 自動生成 Actions:每一種請求狀態都有對應的 Action 被發出。
  4. 快取管理邏輯:它幫你寫好了「如果 Store 裡已經有這份資料,且還沒過期,就不要發請求」的複雜邏輯。

這就是為什麼我們說 RTK Query 是「宣告式(Declarative)」的。 以前用 createAsyncThunk,你得像寫食譜一樣,一步步教電腦怎麼抓資料、怎麼處理錯誤;現在用 RTK Query,你只需要告訴電腦「我的 API 在哪裡,長什麼樣子」,剩下的繁瑣步驟它都幫你封裝好了。

這種「平滑過渡」的意義

這意味著你現有的 Redux 知識完全沒有白費。

  • 你依然可以用 Redux DevTools 監控所有的 API 請求(你會看到 RTK Query 發出的專屬 Actions)。
  • 你的 API 狀態依然存在於那個單一的 Store 中,保持了「單一事實來源」的優良傳統。
  • 你可以同時在一個專案中使用 createSlice 處理 UI 狀態,並用 createApi 處理資料請求,兩者分工明確,互不干擾。

總結與核心心智模型

讓我們用一個簡單的邏輯來總結這一部分:

  1. 分工明確createSlice 管 UI 狀態(你是主人);createApi 管伺服器資料(你是租客)。
  2. 單一事實來源:即使 RTK Query 看起來像是一個獨立的模組,它的資料最終還是會匯流到 Redux Store 中,讓你能統一管理。
  3. 自動化生產線:透過 createApi 定義好規則,它就為你產出 Hooks,消除掉所有重複的非同步樣板程式碼。

建立你的心智模型

在進入下一章實作之前,請記住這個畫面: 你的應用程式是一個大的辦公室。createSlice 是你的辦公桌抽屜,放著你隨手要用的文具(UI 狀態);而 createApi 是你連往外部圖書館的終端機,它幫你檢索資訊、快取書籍,並在書本內容更新時通知你,而你只需要在終端機前下指令(Hooks)即可。

承先啟後

我們已經建立了對 RTK Query 架構的深層認識,知道它與傳統 Slice 的分工。但這台強大的「資料終端機」要如何正式連接到我們的 Redux 體系中呢?

在下一個章節中,我們將深入探討 「整合至 Redux Store」 的細節。我們會看到 api.reducer 該如何掛載,以及最關鍵的 api.middleware 究竟在後台偷偷做了哪些強大的工作,例如快取過期管理與訂閱計數。這將為我們動手寫下第一行 createApi 程式碼打下最後一塊基石。