🔄
Angular Signalsで状態管理がめちゃくちゃ楽になった話
Angular Signalsで状態管理がめちゃくちゃ楽になった話
Angular Signals、最高ですよね。状態管理がめちゃくちゃ楽になりました。これまでReduxとかNgRxとか使ってたけど、正直めんどくさかったんですよね。Signalsを使うようになってから、状態管理の考え方が根本から変わりました。
時間の概念から解放された
RxJSでよくやってた「いつ」状態が変わるかという考え方から解放されました。例えば、こんなコード:
// 昔の書き方(RxJS)
const counter$ = new BehaviorSubject(0);
counter$.pipe(
debounceTime(300),
distinctUntilChanged()
).subscribe(value => {
// 値が変化したときの処理
});
// 今の書き方(Signals)
const counter = signal(0);
effect(() => {
// counterの値が変化するたびに自動的に実行される
console.log(counter());
});
RxJSだと「いつ」状態が変わるかを意識する必要がありましたが、Signalsでは「何が」状態であるかだけを考えればよくなりました。これ、めちゃくちゃ楽です。
状態管理の煩わしさから解放された
ReduxとかNgRxとか使ってたときは、アクション、リデューサー、セレクターとか、めちゃくちゃボイラープレートコード書いてましたよね。Signalsでは、こんな感じでシンプルに書けます:
// 基本的なSignalsの例
const count = signal(0);
const doubledCount = computed(() => count() * 2);
const isEven = computed(() => count() % 2 === 0);
// 状態の更新
count.update(value => value + 1);
状態管理の「管理」という部分が、Signalsによって自動化されました。開発者は、状態が「何であるか」を定義するだけでよくなりました。
実践的な例:カウンターアプリ
実際のコードを見てみましょう。こんな感じで書けます:
import { Component } from '@angular/core';
import { signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<div>
<h2>カウンター: {{ count() }}</h2>
<p>2倍の値: {{ doubledCount() }}</p>
<p>{{ isEven() ? '偶数です' : '奇数です' }}</p>
<button (click)="increment()">増加</button>
<button (click)="decrement()">減少</button>
<button (click)="reset()">リセット</button>
</div>
`,
})
export class CounterComponent {
// ソースデータ
count = signal(0);
// 派生データ
doubledCount = computed(() => this.count() * 2);
isEven = computed(() => this.count() % 2 === 0);
// 副作用
constructor() {
effect(() => {
console.log(`カウントが変更されました: ${this.count()}`);
});
}
// アクション
increment() {
this.count.update(value => value + 1);
}
decrement() {
this.count.update(value => value - 1);
}
reset() {
this.count.set(0);
}
}
これ、めちゃくちゃシンプルですよね。状態管理のコードが減って、ビジネスロジックに集中できるようになりました。
一方向のデータフロー
Signalsを使うと、データが一方向に流れるようになります。例えば:
// ソースデータ(アプリケーションの状態の源)
const sourceData = signal({ user: null, items: [] });
// 派生データ
const user = computed(() => sourceData().user);
const items = computed(() => sourceData().items);
const hasItems = computed(() => items().length > 0);
// UIの状態
const isLoading = signal(false);
const selectedItemId = signal(null);
const selectedItem = computed(() => {
const id = selectedItemId();
return id ? items().find(item => item.id === id) : null;
});
これ、デバッグがめちゃくちゃ楽になりました。データの流れが明確で、どこで問題が起きているかがすぐにわかります。
まとめ
Signalsを使ってみて、以下の点が特に良かったです:
- 時間の概念からの解放 - RxJSの「いつ」から解放されて、コードがすっきり
- 状態管理の煩わしさからの解放 - Reduxとかのボイラープレートコードから解放
- シンプルなデータパイプライン - Signalsのネットワークで状態を表現できる
- 一方向のデータフロー - デバッグが楽になった
- 関数としての関係性 - UIは状態の関数、状態はデータの関数という考え方
小〜中規模のアプリケーションでは、NgRxなどの大きなライブラリを導入せずとも、Signalsだけで十分対応できそうです。これからもSignalsを使い倒していきたいと思います!
それでは、また。
Discussion