Angular PR

【Angular】Signal is the best!?シンプルな状態管理を行う魔法のメソッド

【Angular】Signal is the best!?シンプルな状態管理を行う魔法のメソッド
記事内に商品プロモーションを含む場合があります

こんにちは!

2023年5月のAngular 16のリリースで導入された新機能「Signal」。

新しい状態管理の仕組みらしいけど、従来の方法と何が違うの?
どんなメリットがあるの?
具体的にどう使えばいいの?

このような疑問をお持ちの方も多いのではないでしょうか?

この記事では、Angular Signalの基礎知識から具体的な実装方法、さらにはパフォーマンス改善のテクニックまで、現場で使えるノウハウを詳しく解説します。

この記事は次のような人におすすめ!
  • Angular Signalの基礎から応用まで体系的に学びたい
  • 効率的な状態管理の方法を知りたい
  • パフォーマンスを改善する具体的な方法を探している
  • Signal導入のメリット・デメリットを理解したい
  • コードの具体例を見ながら学習したい

この記事を読めば、Angular Signalの全体像を理解できるだけでなく、実際のプロジェクトでSignalを活用する方法が分かるようになりますよ!

「より良い状態管理の方法を探している方」「パフォーマンスを改善したい方」は、ぜひ参考にしてください。

それでは、順を追って詳しく見ていきましょう!

そもそもSignalとは?

まずは、Angular Signalについて簡単におさらいしておきましょう。

Signalとは、Angular 16で導入された新しい状態管理の仕組みです。主な特徴として、値の変更を自動的に検知し、関連するコンポーネントを効率的に更新できる点が挙げられます。

従来のAngularでは、変更検知(Change Detection)の仕組みを使って状態の更新を検知していました。これは全てのコンポーネントをチェックする必要があり、アプリケーションが大きくなるとパフォーマンスの低下を招く可能性がありました。

一方、Signalは変更があった部分のみを効率的に更新できます。例えば、カウンターアプリケーションで数値を更新する場合、その値を表示しているコンポーネントのみが再レンダリングされるのです。

また、Signalはコンポーネントの外でも使用でき、サービスやストアなどでも活用できます。これにより、アプリケーション全体で一貫した状態管理が可能になります。

Signalを使うメリット

Angular Signalを使用することには、実はたくさんのメリットがあります。ここでは、主なメリットについて詳しく解説します。

パフォーマンスの向上
Signalは変更のあった部分のみを更新するため、従来の変更検知に比べてパフォーマンスが向上します。特に大規模なアプリケーションでは、この最適化の効果が顕著に表れます。
シンプルな状態管理
Signalを使用することで、状態管理がより直感的に行えるようになります。値の更新とそれに伴う画面の更新が自動的に行われるため、開発者が明示的に変更検知を制御する必要がなくなります。
型安全性の向上
TypeScriptと完全に統合されているため、コンパイル時に型チェックが行われます。これにより、実行時のエラーを事前に防ぐことができます。
テストの容易さ
Signalは独立した単位としてテストが可能です。モックやスタブを使用せずに、直接Signal値を操作してテストを書くことができます。

Signalの基本的な使い方

それでは、具体的なSignalの使い方を見ていきましょう。基本的な使用方法から応用まで、順を追って解説します。

Signalの作成

Signalを作成するには、signal()関数を使用します。


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

// 数値のSignalを作成
const count = signal(0);

// オブジェクトのSignalを作成
const user = signal({
  name: 'John',
  age: 30
});

Signalを作成する際は、初期値を設定することができます。また、型を明示的に指定することもできます。

Signalの値を取得・更新

Signalの値を取得するには、Signalを関数として呼び出すだけです。値を更新するには、set()メソッドを使用します。


// 値の取得
console.log(count()); // 出力: 0

// 値の更新
count.set(1);
console.log(count()); // 出力: 1

// オブジェクトの更新
user.set({
  name: 'Jane',
  age: 25
});

値の更新には、update()メソッドを使用することもできます。これは現在の値を基に新しい値を計算する場合に便利です。


// 値の更新(update使用)
count.update(value => value + 1);

コンポーネントでのSignalの使用

コンポーネントでSignalを使用する場合、以下のように実装します。


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

@Component({
  selector: 'app-counter',
  template: `
    <div>
      <p>Count: {{ count() }}</p>
      <button (click)="increment()">Increment</button>
    </div>
  `
})
export class CounterComponent {
  count = signal(0);

  increment() {
    this.count.update(value => value + 1);
  }
}

このように、テンプレート内でSignalを直接参照することができます。Signalの値が変更されると、関連する部分のみが自動的に更新されます。

計算されたSignal(computed)

複数のSignalから新しい値を計算する場合、computed()関数を使用します。


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

const width = signal(10);
const height = signal(20);

const area = computed(() => width() * height());

console.log(area()); // 出力: 200

// widthを更新すると、areaも自動的に更新される
width.set(15);
console.log(area()); // 出力: 300

computedは依存するSignalの値が変更されると自動的に再計算されます。これにより、複雑な計算や変換を効率的に行うことができます。

Signalの応用的な使い方

基本的な使い方を理解したところで、より応用的な使い方を見ていきましょう。

非同期処理との組み合わせ

Signalは非同期処理とも柔軟に組み合わせて使用できます。


import { signal, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

export class UserService {
  private http = inject(HttpClient);
  users = signal<User[]>([]);

  async fetchUsers() {
    try {
      const data = await this.http.get<User[]>('/api/users').toPromise();
      this.users.set(data);
    } catch (error) {
      console.error('Error fetching users:', error);
    }
  }
}

SignalのEffect

effect()関数を使用すると、Signalの値が変更されたときに特定の処理を実行することができます。


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

const count = signal(0);

effect(() => {
  console.log(`Count changed to: ${count()}`);
});

// effectが実行される
count.set(1); // ログ出力: "Count changed to: 1"
count.set(2); // ログ出力: "Count changed to: 2"

これは、値の変更時にログを出力したり、副作用を実行したりする場合に便利です。

Signalのコレクション操作

配列やオブジェクトのSignalを扱う場合は、イミュータブルな更新を心がけましょう。


const items = signal<string[]>([]);

// 配列に新しい要素を追加
items.update(current => [...current, 'new item']);

// 配列から要素を削除
items.update(current => current.filter(item => item !== 'target'));

// 配列の要素を更新
items.update(current =>
  current.map(item =>
    item === 'target' ? 'updated item' : item
  )
);

サービスでのSignal活用

Signalはサービスでも活用できます。これにより、アプリケーション全体で状態を共有することができます。


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

@Injectable({
  providedIn: 'root'
})
export class ThemeService {
  private theme = signal<'light' | 'dark'>('light');

  getTheme() {
    return this.theme;
  }

  toggleTheme() {
    this.theme.update(current =>
      current === 'light' ? 'dark' : 'light'
    );
  }
}

パフォーマンス最適化のポイント

Signalを使用する際のパフォーマンス最適化について、いくつかのポイントを紹介します。

適切な粒度でSignalを作成する

Signalは必要な粒度で作成することが重要です。大きすぎる状態を1つのSignalで管理すると、小さな変更でも大きな再計算が発生する可能性があります。


// 悪い例
const user = signal({
  personalInfo: { name: 'John', age: 30 },
  settings: { theme: 'dark', language: 'en' },
  posts: [/* 大量のポストデータ */]
});

// 良い例
const personalInfo = signal({ name: 'John', age: 30 });
const settings = signal({ theme: 'dark', language: 'en' });
const posts = signal([/* 大量のポストデータ */]);

不要な再計算を避ける

computedを使用する際は、依存関係を最小限に抑えることが重要です。


// 悪い例
const userDetails = computed(() => {
  // 全てのユーザー情報を計算に含める
  const user = userSignal();
  return `${user.name} (${user.age}) - ${user.location}`;
});

// 良い例
const userDisplayName = computed(() => {
  // 必要な情報のみを使用
  const { name, age } = userSignal();
  return `${name} (${age})`;
});

メモ化の活用

頻繁に計算が必要ない値は、メモ化を活用することでパフォーマンスを向上させることができます。


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

const items = signal<number[]>([1, 2, 3, 4, 5]);

// 重い計算をメモ化
const sum = computed(() => {
  console.log('Calculating sum...');
  return items().reduce((acc, curr) => acc + curr, 0);
});

// sumの値は一度計算されると、itemsが変更されるまでキャッシュされる
console.log(sum()); // 計算実行
console.log(sum()); // キャッシュから返される

まとめ

ここまで、Angular Signalについて詳しく解説してきました。改めて、重要なポイントをおさらいしましょう。

  • Signalは効率的な状態管理を実現する新機能
  • 変更のあった部分のみを更新し、パフォーマンスを向上
  • シンプルで直感的なAPI設計により、開発効率が向上
  • TypeScriptとの完全な統合により、型安全性が確保
  • 適切な使用方法とパフォーマンス最適化が重要

Signalは、フロントエンド開発における新しい可能性を開く技術です。特に大規模なアプリケーションでは、その効果が顕著に表れます

今回の記事で紹介した基礎知識や具体的な実装方法を参考に、ぜひご自身のプロジェクトでも活用してみてください。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です