iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Modern Web

Angular 進階實務 30天系列 第 18

Day 18:狀態管理 - 狀態變更(State Changes) → 資料怎麼被修改?

  • 分享至 

  • xImage
  •  

前言

在前兩篇文章中,我們探討了:

  • Day 16:資料儲存 → 資料應該放在哪裡?
  • Day 17:資料流控制 → 資料應該怎麼在應用程式中流動?

接下來要討論的是狀態管理中最關鍵的問題:

👉 「狀態應該如何被修改?」

這個問題看似簡單,實際上卻是前端開發中容易踩坑的地方。

舉例來說:

  • 你在一個地方改了物件屬性,結果另一個地方 UI 沒有更新。
  • 多個服務同時修改狀態,導致最終資料來源不明。
  • 沒有規範修改流程,導致專案越做越大就越難維護。

要避免這些問題,我們需要建立一套 明確且可追蹤的狀態變更策略


一、狀態變更的三大來源

在 Angular 中,狀態的改變大致可以歸類為三種來源:

  1. 使用者操作觸發 (User Trigger)
    • 透過 Template Binding,例如 (click)(input)
    • 表單輸入、按鈕點擊、選單切換,都是最常見的觸發點
  2. 系統事件觸發 (System Trigger)
    • HttpClient 回傳資料
    • Router.events 監聽導航切換
    • 計時器或 WebSocket 推播
  3. 集中式更新 (Centralized Update)
    • 使用 SignalsRxJS Subject 作為狀態容器
    • 透過 signal.set().next() 主動更新
    • 適合跨元件共享或事件驅動的情境

這三種來源涵蓋了絕大多數的狀態變化場景。下面我們就用 Angular 原生 API 來看看這些更新是怎麼發生的。


二、使用者操作觸發

1. Template Binding

Angular 提供了非常直覺的事件繫結語法:

<button (click)="count = count + 1">增加</button>
<input (input)="onInput($event)" />

這種方式最簡單,直接在 Template 綁定事件就能改變狀態。

優點是快速、直觀;缺點是如果邏輯過多,容易分散在多處,不利集中管理。

2. Reactive Forms API

當我們處理表單時,會常用到 FormControlFormGroup

this.form = this.fb.group({
  username: ['', Validators.required],
});

this.form.patchValue({ username: 'Zoe' });

這裡的 patchValue 就是一種 mutable 更新,直接修改表單內部的狀態模型。

優點是 API 完整,適合中大型表單;缺點是小表單會覺得有點繁瑣。


三、系統事件觸發

1. HttpClient

呼叫 API 是狀態更新的核心場景:

this.http.get('/api/orders').subscribe(res => {
  this.orders = res; // 狀態更新
});

這裡的更新可以用 immutable(建立新陣列),也可以用 mutable(直接修改現有物件)。

2. Router Events

導航事件也是一種狀態改變:

this.router.events.subscribe(e => {
  if (e instanceof NavigationEnd) {
    this.currentUrl = e.url;
  }
});

這樣能在頁籤或側邊欄中即時反映目前所在位置。


四、集中式更新

1. Signals (Angular 16+)

Angular 16 推出的 Signals,是未來狀態管理的核心方向。

import { signal } from '@angular/core';

count = signal(0);

increase() {
  this.count.set(this.count() + 1);
}

這裡的 set 就是狀態變更。Signals 自動追蹤依賴,會讓使用的地方自動更新,省去大量手動處理。

2. RxJS Subject

Angular 本身大量使用 RxJS,因此 Subject 也是常見的狀態更新工具:

private userSubject = new BehaviorSubject<User | null>(null);
user$ = this.userSubject.asObservable();

updateUser(user: User) {
  this.userSubject.next(user);
}

這裡呼叫 .next(),所有訂閱者就會收到更新。

適合事件驅動、跨元件通知、即時資料流。

類別 Angular 工具 / API 更新方式 策略 (Mutable / Immutable) 適用場景
使用者操作觸發 Template Binding(click), (input) 直接在事件中修改狀態 Mutable 表單輸入、按鈕操作
Reactive Forms APIsetValue, patchValue 呼叫表單方法更新模型 Mutable 登入、註冊、訂單編輯
系統事件觸發 HttpClient API 回應後指派值 常用 Immutable(取代整個陣列/物件) 清單載入、CRUD
Router EventsNavigationEnd 監聽路由事件後更新狀態 Mutable 或 Immutable 分頁切換、導航紀錄
集中式更新 Signals (Angular 16+)signal.set() 更新 signal 狀態容器 Mutable UI 切換、輕量共享狀態
RxJS Subject / BehaviorSubject.next() 推送新值給所有訂閱者 通常 Immutable 即時資料、跨元件通知
狀態策略 展開運算子 / map / filter 建立新物件或新陣列 Immutable 清單操作、OnPush 變更檢測
直接修改屬性obj.name = 'Zoe' 原地改動 Mutable 單純 UI 狀態、表單控制

五、狀態更新策略:Mutable vs Immutable

前面提到的每一種更新方式,都可以再進一步區分成 mutableimmutable 兩種策略。

1. Mutable 更新

直接改變既有的物件或陣列:

this.form.patchValue({ name: 'Zoe' }); // 改變 FormControl 狀態
this.count.set(5);                     // Signals set
  • 優點:直覺、效能好
  • 缺點:不容易追蹤歷史狀態,可能造成難以 debug

2. Immutable 更新

建立一份新物件或陣列,取代舊的:

this.orders = [...this.orders, newOrder]; // 建立新陣列
const updated = { ...this.user, name: 'Zoe' };
  • 優點:狀態可追蹤、利於 Debug/Undo/Redo、搭配 OnPush 變更檢測更穩定
  • 缺點:需要額外的記憶體,對效能有一點點影響

3. 如何選擇?

  • mutable 常見用法:需要常常修改值的情況
    • 表單
    • Signals
  • immutable 常見用法:以展示類型的資料為主,或是需要原資料的情況
    • 清單
    • 陣列
    • 需要比對的狀態

六、小結

這一篇我們回答了第三個問題:

👉 「資料什麼時候、怎麼被改變?」

整理如下:

  1. 觸發點
    • Template Binding
    • Reactive Forms API
    • HttpClient / Router Events
  2. 集中式更新
    • Signals:新一代反應式狀態容器
    • RxJS Subject:事件驅動的推送機制
  3. 策略
    • Mutable:直覺但難追蹤
    • Immutable:可追蹤但有額外成本

上一篇
Day 17:狀態管理 - 資料流控制 (Data Flow Control) → 資料怎麼流動?
系列文
Angular 進階實務 30天18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言