iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0
佛心分享-IT 人職涯歷練

從 UI/UX 麻瓜到工程魔法師|從讀懂程式開始系列 第 20 篇

Day 20|明明有 reload(),為什麼資料就是不重新抓?Cache、Request Key 到底在幫忙還是在搗亂?

  • 分享至 

  • xImage
  •  

上一篇講到:

API 重複 Request,不一定是 API 本身有問題,也可能是 Component 一直被 remount。

今天來看一個剛好相反的狀況:

我明明叫它重新抓資料了,為什麼它就是不抓?

這種 Bug 其實一樣很折磨人。

因為表面上看起來:

按鈕有按
↓
reload() 有執行
↓
畫面卻沒變

第一個反應很容易是:

reload() 壞掉了?

但實際上,問題可能出在:

Request 被 cache 住了

或:

Request 被某個 key 視為「同一筆」

先看最簡單的情況

假設:

const { data, reload } = useRequest(
  () => UserService.getList()
);

然後:

<button onClick={() => reload()}>
  Reload
</button>

理想流程:

Click
↓
reload()
↓
重新執行 Request
↓
拿到新資料
↓
更新畫面

這個概念沒有問題。

但真實專案裡,Request Hook 通常不只做:

打一支 API

它可能還做:

cache
dedupe
loading 管理
request key
資料共享

所以事情就開始變複雜。


Cache 可以先理解成「先把結果記住」

例如:

第一次 Request

GET /users
↓
Response A

↓
Cache 記住

下一次如果系統認為:

這還是同一個 Request

它可能直接拿:

Response A

而不是重新打 API。

這通常是為了:

減少重複 Request
提升速度
降低 Server 負擔

所以 Cache 本身不是壞東西。

真正的問題是:

它怎麼判斷「這是同一個 Request」?


這時候就會遇到 Request Key

可以把 Request Key 想成:

這次 Request 的身份證。

例如:

GET /users?page=1

可能有一個 key:

users-page-1

如果下一次又看到:

users-page-1

系統就可能認為:

我已經有這份資料了。


但如果 key 太固定,就可能把本來應該重新抓的資料擋掉

例如:

useRequest(fetchUsers, {
  requestKey: "user-list",
});

假設這個 key:

永遠都是 user-list

但實際上資料已經因為:

新增
編輯
刪除
Filter 改變

而改變了。

如果 Request 系統還是把:

user-list

當成同一份快取資料,

那:

reload()

看起來有執行,

但拿到的可能還是舊結果。


問題不一定是 reload() 沒跑

這裡我覺得最重要。

Debug 時要分兩件事:

reload() 有沒有被呼叫?

和:

reload() 之後,Request 有沒有真的重新出去?

不是同一件事。

可以畫成:

Click
↓
reload()
↓
Request Hook
↓
Cache 判斷
├─ 使用 cache
└─ 真正發 API

所以如果只看到:

console.log("reload")

有印,

只能證明:

function 被呼叫了

不能證明:

HTTP Request 真的送出了

這時候 Network 比 console 更重要

如果懷疑:

reload 沒有效果

我現在會直接看:

DevTools
↓
Network

然後按:

Reload

看有沒有新的 HTTP Request。

情況一

有新的 Request

那:

reload()

其實有正常工作。

接下來要看:

Response 是新的嗎?
State 有更新嗎?
UI 有吃到新的 data 嗎?

情況二

完全沒有新的 Request

那才要繼續往:

cache
request key
manual
dedupe
hook 設定

查。


「畫面沒變」也不等於「沒重新 Request」

例如:

reload()
↓
HTTP Request 成功
↓
Response 跟上次一樣
↓
畫面看起來沒變

這種也很常見。

所以 Debug 不要直接:

畫面沒變 = API 沒打。

而是分層:

Function
↓
HTTP Request
↓
Response
↓
State
↓
Render

每一層都要分開確認。


我以前很容易直接去改 Request 邏輯

例如看到:

reload 失效

就想:

加新的 key
改 reqKey
關 cache
強制重新抓

這些做法有時候真的可以讓畫面恢復正常。

但有一個風險:

我可能只是把症狀壓掉,卻沒有理解原本 Request 機制。

例如:

原本 cache 設計其實有用途

結果我為了某個畫面:

把 cache 整個關掉

反而可能造成:

其他地方多打 Request
效能變差
原本共用資料失效

所以這類問題我會先問:「這個設定原本是為了解決什麼?」

例如看到:

requestKey
cacheKey
reqKey

不要先刪。

先問:

誰加的?
↓
它在防什麼?
↓
它影響哪些 Request?
↓
reload 和它的關係是什麼?

這個步驟很重要。

因為專案裡很多「看起來多餘的設定」,

可能都是之前為了另一個 Bug 加的。


Request Key 如果跟資料條件有關,就要思考 key 是否完整

例如:

Page 1
Filter = ACTIVE

和:

Page 1
Filter = INACTIVE

如果兩個 Request 都被當成:

user-list

就可能發生:

不同查詢條件
↓
卻共用同一份 cache

比較合理的識別概念可能是:

user-list-page1-active

和:

user-list-page1-inactive

也就是:

會影響 Response 的條件,理論上也可能需要影響 Request 的 identity。

但實際怎麼設計,還是要看使用的 Request Library。


這裡跟昨天的 Component identity 有點像

昨天我們講:

React key

是在回答:

這是不是同一個 Component / List Item?

今天的:

Request key

概念上也有點像:

這是不是同一個 Request?

雖然兩個不是同一個 API,也不是同一個機制,

但它們都在做一件事:

Identity

也就是:

系統要怎麼判斷「這是不是同一份東西」?


真實 Debug 時,我會這樣拆

假設情境:

修改資料成功
↓
呼叫 reload()
↓
列表還是舊資料

第一步:

確認修改 API 成功嗎?

看:

PATCH / POST Response

第二步:

reload() 有被呼叫嗎?

可以:

console.log()

或 breakpoint。


第三步:

Network 有沒有新的 GET?

如果沒有:

往 Request Hook / cache 查

第四步:

如果有 GET:

Response 是舊的還是新的?

Response 還是舊的

可能往:

Backend
Database
Server Cache

查。

Response 已經是新的

那:

Backend 大概沒問題

往:

State
dataSource
Render
memo

查。


可以畫成一條完整路線

Save
↓
PATCH Request
↓
PATCH Success
↓
reload()
↓
GET Request 有出去嗎?
├─ No
│  ↓
│  Request Hook / Cache / Key
│
└─ Yes
   ↓
   Response 是新的嗎?
   ├─ No
   │  ↓
   │  Backend / Database / Server Cache
   │
   └─ Yes
      ↓
      State 有更新嗎?
      ↓
      UI 有吃新的 State 嗎?

這樣 Debug 就不會變成:

一直亂改 reload。


Cache 不是敵人

這點我覺得也很重要。

Cache 很常被誤會成:

啊又是 cache 害的。

但其實它只是:

用舊結果換取速度

真正要處理的是:

什麼時候可以用舊資料?什麼時候一定要重新抓?

這是一個資料一致性的問題。


reload() 也不是萬能刷新按鈕

我以前會把:

reload()

理解成:

強制所有東西重新來一次。

但其實它通常只是:

要求某個 Request Hook 重新執行它自己的流程。

而這個流程裡還可能有:

cache
參數
key
request lifecycle
library 行為

所以看到:

reload()

也要繼續往裡面看:

它到底 reload 了什麼?


今天真正想記的是

遇到:

reload() 有執行
但資料沒更新

不要直接得出:

reload 壞了。

而是依序問:

① reload 有被呼叫嗎?

② HTTP Request 真的重新送出了嗎?

③ Response 是新資料嗎?

④ State 有更新嗎?

⑤ UI 使用的是這份 State 嗎?

如果連:

Request 都沒出去

才開始查:

cache
request key
hook 設定

這樣比較不容易在錯誤的層一直修改。


這兩天其實都在練同一件事

Day 19:

Request 打太多
↓
不要急著擋 Request
↓
先查 Component lifecycle

Day 20:

Request 沒有重新打
↓
不要急著怪 reload
↓
先查 Request lifecycle

共同核心都是:

不要只看症狀發生在哪一層,要把觸發流程完整畫出來。

當我開始這樣 Debug 之後,

很多原本看起來很玄學的:

React 怎麼又打 API?
為什麼不 reload?
為什麼資料還是舊的?

就會慢慢變成:

到底是哪一層沒有照我以為的方式執行?

這個問題通常比:

「要加哪段 Code?」

更接近真正的 Root Cause。

下一篇可以接一個資料跨層之後非常容易變形的實戰問題:

多選明明是 Array,為什麼放進 URL 再回來後,竟然變成 String?


上一篇
Day 19|Filter 只是展開,為什麼 API 卻又打了一次?
系列文
從 UI/UX 麻瓜到工程魔法師|從讀懂程式開始 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言