前言
在探索 React 官方文件的過程中,我注意到文件在講解 State 時特別強調了一個重要概念:將 State 視為不可變(Immutable)。這個觀念初看之下或許不太容易理解,畢竟在 JavaScript 中我們習慣直接修改物件的內容。但隨著對 React 的深入學習,漸漸體會到這個設計背後的巧思。
我們就來透過官方文件來理解這個吧
State 不可變性的重要性
在 React 中,雖然技術上可以直接修改 State 中的物件(這稱為 mutation),但這樣做會帶來許多潛在的問題。讓我們看看為什麼要將 State 視為不可變(immutable),以及該如何正確地更新物件型的 State。
錯誤示範
1 | function ProfileEditor({ person, setPerson }) { |
正確做法
1 | function ProfileEditor({ person, setPerson }) { |
為什麼要保持不可變性?
堅持 State 的不可變性能帶來許多好處:
- 更容易除錯
- 每次更新都會生成新物件,方便比對前後的 State,快速定位問題。例如,如果某個功能失效,我們可以輕鬆檢查更新前後的資料來找出問題。
- 提升效能
- React 透過淺層比較物件的參照(reference)判斷變化,比深層逐項比較內容快得多。
- 不可變性確保每次更新都是新物件,幫助 React 快速決定是否需要重新渲染,避免不必要的計算。
- 簡化開發流程
- 遵循不可變原則讓程式行為更可預測,降低處理物件參照和資料同步的複雜度。例如,我們不需要擔心資料被其他部分意外修改。
- 測試變得更簡單,因為每個狀態都是獨立的快照,可以用來還原和重現應用中的問題。
處理巢狀物件
在開發中,經常會遇到需要處理巢狀物件的情境。例如,當我們的狀態是一個多層結構的物件時,正確地更新某一層的資料尤為重要。如果直接修改原始物件,會破壞不可變性,導致潛在問題。
以下是一個範例,展示如何正確更新巢狀物件的狀態:
1 | const [person, setPerson] = useState({ |
為什麼這樣做?
- 不可變性:React 需要依靠物件參照的改變來判斷是否重新渲染。
- 防止潛在問題:直接修改原始物件會造成狀態同步的混亂,甚至影響其他元件。
物件巢狀層級過深的解法
當物件結構過於複雜時,上述寫法可能變得冗長且難以維護。以下是兩種解決方法:
扁平化資料結構
將巢狀物件轉換為更扁平的形式,例如使用唯一鍵值組合標示層級:
1
2
3
4
5
6
7const [person, setPerson] = useState({
name: '小明',
contactEmail: 'ming@example.com',
contactCity: '台北',
contactStreet: '信義路',
});
使用函式庫簡化邏輯
例如使用
immer
函式庫,它可以用更直觀的方式撰寫更新邏輯:1
2
3
4
5
6import produce from 'immer';
setPerson(produce(person, draft => {
draft.contact.address.city = '高雄';
}));
總結
在 React 中正確處理物件型的 State 是很重要的技巧。透過遵循不可變性原則,我們可以讓程式碼更容易維護、除錯,同時也能享受到 React 提供的各種優化機制。
雖然剛開始可能會覺得這樣寫比較麻煩,但這些「限制」其實都是為了幫助我們寫出更穩定、可靠的程式碼。隨著專案規模成長,你會發現這些良好的實踐方式帶來的好處遠大於一開始的不便。