前言
在專案開發的過程中,處理副作用一直是個重要的課題。記得剛接觸 React 時,總是把 useEffect 跟傳統的生命週期掛鉤,導致寫出來的程式碼既複雜又難以維護。直到後來深入了解了 Effect 的本質 - 同步機制,才發現原來我們一直用錯了方向。今天就讓我分享這個重要的觀念轉變,以及在實務上如何正確運用 Effect。
Effect 與元件生命週期的差異
傳統上,React 元件的生命週期分為:
- Mount(掛載):元件首次被加入畫面
- Update(更新):因 props 或 state 變更而重新渲染
- Unmount(卸載):元件從畫面中移除
但 Effect 的運作方式其實完全不同:
1 | function ChatRoom({ roomId }) { |
Effect 的同步機制
重新同步的時機
Effect 會在以下情況重新執行:
- 依賴項改變
- 元件重新渲染(只在依賴項發生變化時)
同步的過程是這樣的:
- 先執行清理函式,停止舊的同步
- 然後執行 Effect 主體,開始新的同步
1 | function VideoPlayer({ src, isPlaying }) { |
響應式值與依賴關係
什麼是響應式值?
在 React 中,響應式值包括:
- Props:從父元件傳入的值
- State:使用 useState 建立的狀態
- 計算值:由 props 或 state 衍生的值
1 | function DataFetcher({ query, pageSize }) { |
依賴項的重要性
遺漏依賴項可能導致:
- 使用到過期的值
- 無法及時反應資料變化
- 產生不預期的行為
進階概念:全域和可變值
特殊情況處理
有些值不適合作為依賴項:
- 全域變數(如 window.location)
- Ref 的 current 屬性
- 可變物件
處理這類值的方式:
1 | function WindowTracker() { |
最佳實踐
Effect 設計原則
- 每個 Effect 只負責一個獨立的同步邏輯
- 提供完整的清理機制
- 確保能處理多次執行的情況
常見陷阱與解決方案
1
2
3
4
5
6
7
8
9
10// ❌ 避免這樣做
useEffect(() => {
setCount(count + 1);
}, [count]);
// ✅ 改用 state updater
useEffect(() => {
setCount(c => c + 1);
}, []);效能考量
- 避免不必要的依賴
- 適當拆分 Effect
- 使用記憶化(useMemo)減少重複計算
總結
理解 Effect 的同步本質,能幫助我們:
- 寫出更清晰的副作用處理邏輯
- 避免生命週期的思維陷阱
- 正確處理資源的訂閱與清理
- 建立更容易維護的程式碼
最重要的是要記住:Effect 不是生命週期事件,而是一個同步的過程。當我們轉換這個思維模式,很多原本覺得複雜的問題都會變得清晰許多。