Jeff的隨手筆記

學習當一個前端工程師

0%

『Day 15』在 React 中正確處理物件型 State

前言

在探索 React 官方文件的過程中,我注意到文件在講解 State 時特別強調了一個重要概念:將 State 視為不可變(Immutable)。這個觀念初看之下或許不太容易理解,畢竟在 JavaScript 中我們習慣直接修改物件的內容。但隨著對 React 的深入學習,漸漸體會到這個設計背後的巧思。

我們就來透過官方文件來理解這個吧

State 不可變性的重要性

在 React 中,雖然技術上可以直接修改 State 中的物件(這稱為 mutation),但這樣做會帶來許多潛在的問題。讓我們看看為什麼要將 State 視為不可變(immutable),以及該如何正確地更新物件型的 State。

錯誤示範

1
2
3
4
5
6
7
8
function ProfileEditor({ person, setPerson }) {
function handleNameChange(e) {
// ❌ 錯誤示範:直接修改 state
person.name = e.target.value;
setPerson(person);
}
// ...
}

正確做法

1
2
3
4
5
6
7
8
9
10
function ProfileEditor({ person, setPerson }) {
function handleNameChange(e) {
// ✅ 正確做法:建立新物件
setPerson({
...person,
name: e.target.value
});
}
// ...
}

為什麼要保持不可變性?

堅持 State 的不可變性能帶來許多好處:

  • 更容易除錯
    • 每次更新都會生成新物件,方便比對前後的 State,快速定位問題。例如,如果某個功能失效,我們可以輕鬆檢查更新前後的資料來找出問題。
  • 提升效能
    • React 透過淺層比較物件的參照(reference)判斷變化,比深層逐項比較內容快得多。
    • 不可變性確保每次更新都是新物件,幫助 React 快速決定是否需要重新渲染,避免不必要的計算。
  • 簡化開發流程
    • 遵循不可變原則讓程式行為更可預測,降低處理物件參照和資料同步的複雜度。例如,我們不需要擔心資料被其他部分意外修改。
    • 測試變得更簡單,因為每個狀態都是獨立的快照,可以用來還原和重現應用中的問題。

處理巢狀物件

在開發中,經常會遇到需要處理巢狀物件的情境。例如,當我們的狀態是一個多層結構的物件時,正確地更新某一層的資料尤為重要。如果直接修改原始物件,會破壞不可變性,導致潛在問題。

以下是一個範例,展示如何正確更新巢狀物件的狀態:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const [person, setPerson] = useState({
name: '小明',
contact: {
email: 'ming@example.com',
address: {
city: '台北',
street: '信義路'
}
}
});

// 正確做法:使用展開運算符創建每一層的新物件
setPerson({
...person,
contact: {
...person.contact,
address: {
...person.contact.address,
city: '高雄'
}
}
});

為什麼這樣做?

  • 不可變性:React 需要依靠物件參照的改變來判斷是否重新渲染。
  • 防止潛在問題:直接修改原始物件會造成狀態同步的混亂,甚至影響其他元件。

物件巢狀層級過深的解法

當物件結構過於複雜時,上述寫法可能變得冗長且難以維護。以下是兩種解決方法:

  1. 扁平化資料結構

    • 將巢狀物件轉換為更扁平的形式,例如使用唯一鍵值組合標示層級:

      1
      2
      3
      4
      5
      6
      7
      const [person, setPerson] = useState({
      name: '小明',
      contactEmail: 'ming@example.com',
      contactCity: '台北',
      contactStreet: '信義路',
      });

  2. 使用函式庫簡化邏輯

    • 例如使用 immer 函式庫,它可以用更直觀的方式撰寫更新邏輯:

      1
      2
      3
      4
      5
      6
      import produce from 'immer';

      setPerson(produce(person, draft => {
      draft.contact.address.city = '高雄';
      }));

總結

在 React 中正確處理物件型的 State 是很重要的技巧。透過遵循不可變性原則,我們可以讓程式碼更容易維護、除錯,同時也能享受到 React 提供的各種優化機制。

雖然剛開始可能會覺得這樣寫比較麻煩,但這些「限制」其實都是為了幫助我們寫出更穩定、可靠的程式碼。隨著專案規模成長,你會發現這些良好的實踐方式帶來的好處遠大於一開始的不便。