有夠任性。
簡述
這篇其實是要介紹兩個東西:
- clean function(useEffect) 的適用時機
- AbortController
說真得我還蠻少用到這兩樣東西的,所以想記錄一下。
範例
假設有一個 Component 的內容是透過 useEffect 來從 API 取得資料的。那麼「如果我在 fetch 的期間就把 Component unmount 的話」,會發生什麼事?
這邊先看一段 code,會比較好懂這是什麼意思:
1 | import Button from "Button"; |
這邊不用管 <TripList />
是什麼,只要知道他會用 useEffect
來打 API 拿資料就好。
至於 <Button>
的作用是把 <TripList />
從畫面上移除,也就是前面說的 unmount。
所以現在的情況是,如果我在 <TripList />
還在 fetch 的期間就按下 <Button />
的話,會發生什麼事?
沒意外的話你應該會得到這個訊息:
Warning: Can’t perfrom a React state update on an unmounted component. This is a no-op, …..
附註:後來的 React 似乎有做一點調整,所以不會看到這個訊息。
他的意思是說,「我現在要更新 state,但那個 Component 已經不存在了,安捏嘎丟?」
我們可以先思考一下整個流程,就會比較清楚為什麼會是這種結果了:
- 一開始
showTripList
為 true,所以會渲染<TripList />
<TripList />
開始 fetch 資料- 按下
<Button />
把<TripList />
從畫面上移除掉(unmount) - fetch 回來了,更新原本用來儲存資料的 state
- 發現 Component 不存在,所以顯示 Warning
這邊理解後,就可以繼續往下思考該怎麼解決了。
要怎麼在元件 unmount 的時候把 fetch 取消?
這個是最核心的問題,所以才刻意用標題來醒目。不過我其實在一開始就有先洩漏了,就是透過 clean function
跟 AbortController
來實現。
要 trigger clean function 的方式很簡單,就是「在 useEffect 裡面回傳一個 function」,像這樣:
1 | useEffect(() => { |
而要取消 fetch 的方式就是用 AbortController
來做連結。
直接來看範例吧:
1 | import { useEffect, useState } from "react"; |
改成這樣以後,如果在 fetch 的狀態下點了 <Button />
,就會把 <TripList />
給移除掉,執行 () => controller.abort()
把 fetch 給取消掉。
取消的同時會拋出一個 AbortError
的 Exception,所以會進到 cache
區塊。但要注意,請務必記得多做一層「是不是 AbortError」的判斷,為什麼?
因為我們就是希望 Abort 的情況下不要更新 state,所以才要檢查 Error 的型別,把它跟一般的錯誤處理區別開來。
總而言之,如果還是似懂非懂的話,可以到我寫的 範例 來試試看。希望之後能善用這項技術寫出更好的 code。