前言
在前幾篇文章中,我們討論了 React 的狀態管理機制,包括狀態的快照特性以及批次更新的概念。今天想要分享一個在處理陣列型態狀態時常被忽略,但卻非常重要的觀念:不可變更新(Immutable Update)。這個觀念讓我在剛開始學習 React 時感到困惑,但理解後對於寫出更穩定的 React 應用程式有很大的幫助。
React 的淺比較機制
在深入討論之前,我們需要先了解 React 是如何判斷狀態是否有變化。React 使用了一個叫做「淺比較」的機制,它的運作方式其實很直觀:
- 對於基本型別(數字、字串、布林),React 會直接比較它們的值
- 對於物件和陣列,React 只會比較它們的參考位址,而不會深入比較內容
讓我們用一個簡單的例子來理解:
1 | const arr1 = ['蘋果', '香蕉']; // 新的記憶體位置 |
這個特性告訴我們一個重要的事實:當我們在 React 中直接修改陣列時(例如使用 push
方法),雖然內容改變了,但陣列本身的參考位址維持不變。
而 React 在判斷狀態是否更新時,是透過比較參考位址來判斷的。
就像是我們修改了筆記本的內容,但對 React 來說,它只會確認「是不是同一本筆記本」,而不會去檢查筆記本裡的內容是否改變了。
這就解釋了為什麼直接修改陣列內容時,即使我們用 setState
更新狀態,React 可能還是會認為「這是同一個陣列,沒有變化」,進而不觸發重新渲染。這也是為什麼我們需要建立新的陣列來更新狀態,而不是直接修改原有的陣列。
常見的錯誤與正確做法
在實務開發中,我曾經看過(也寫過!)這樣的程式碼:
1 | // ❌ 錯誤示範:直接修改原陣列 |
在第一個例子中,雖然我們確實修改了陣列的內容,但因為陣列的參考位址沒有改變,React 可能無法察覺到這個變化。而第二個例子中,我們透過展開運算符創建了一個全新的陣列,這樣 React 就能正確偵測到變化了。
處理巢狀結構
在實際開發中,我們常常會遇到更複雜的資料結構,像是陣列中包含物件的情況:
1 | function TodoList() { |
這種方式的好處
採用不可變更新的方式來處理陣列狀態,帶來了幾個重要的優勢:
- 可預測性:避免直接修改原陣列可能帶來的意外副作用
- 效能優化:React 能夠正確地追蹤變化,只更新真正需要更新的部分
- 除錯方便:每次更新都會產生新的參考,讓我們更容易追蹤資料的變化
總結
在 React 中處理陣列狀態時,記得要遵循不可變更新的原則。雖然這樣寫可能會多打一些程式碼,但這些「額外」的工作其實是在幫助我們建立更穩定、可維護的應用程式。理解並善用這個概念,能讓我們在處理複雜的狀態更新時更加得心應手。