Dev Study
← 解説「untracked — 依存に数えずにシグナルを読む」に戻る

サンプルコードで身につける: untracked — 依存に数えずにシグナルを読む

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1依存に入る読み方・入らない読み方

effectの中でのシグナルの読み方は2通りあります。普通に読めば依存に登録され、untrackedで包めば登録されません。まずこの対比を最小の形で確認します。

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

const a = signal(1);
const b = signal(10);

effect(() => {
  const valueA = a();                      // 依存に入る
  const valueB = untracked(() => b());      // 依存に入らない
  console.log(valueA, valueB);
});

a.set(2);  // → effect が再実行される
b.set(20); // → 何も起きない(依存ではないから)

2ログ送信のきっかけを1つに絞る

ページ遷移のログを送るeffectで、ユーザー名も本文に含めたいケースです。untrackedが無いと、ユーザー名の変更でも「遷移していないのにログが飛ぶ」誤動作になります。

import { Component, signal, effect, untracked, inject } from '@angular/core';
import { AnalyticsService } from './analytics.service';

@Component({ selector: 'app-tracker', template: '' })
export class TrackerComponent {
  private analytics = inject(AnalyticsService);
  page = signal('home');
  userName = signal('kazu');

  constructor() {
    effect(() => {
      const page = this.page(); // きっかけはページ遷移だけ
      const name = untracked(() => this.userName());
      this.analytics.send({ page, name });
    });
    // userName.set('matsumoto') してもログは飛ばない
  }
}

3computed の中でも使える

untrackedはeffect専用ではなく、computedの中でも同じように働きます。「件数が変わったときだけメッセージを作り直したい。文面に使う名前の変更では作り直さなくてよい」という例です。

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

const count = signal(3);
const userName = signal('kazu');

const message = computed(() => {
  const n = count(); // これが変われば再計算
  const name = untracked(() => userName()); // 参考情報
  return `${name} さん、未読が ${n} 件あります`;
});

console.log(message()); // 'kazu さん、未読が 3 件あります'
userName.set('matsumoto');
console.log(message()); // 変わらない(再計算されていない)
count.set(5);
console.log(message()); // 'matsumoto さん、未読が 5 件あります'

4シグナルを読む関数ごと包む

untrackedには関数をまるごと渡せます。中で他のシグナルを読んでいるサービスのメソッドを呼ぶとき、意図しない依存が紛れ込むのを防げます。「このeffectのきっかけは selectedId だけ」と明確に宣言できます。

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

// cart.summary() は内部で items() や coupon() を読んでいるとする
effect(() => {
  const id = this.selectedId(); // きっかけはこれだけ

  untracked(() => {
    // この中でいくつシグナルが読まれても依存には入らない
    const summary = this.cart.summary();
    this.logger.log(`選択 ${id} 時点のカート: ${summary}`);
  });
});

5乱用すると更新されない画面になる

untrackedの使いすぎは「値を変えたのに反映されない」という一番調べにくいバグを生みます。派生値の計算に使う値は素直に依存させるのが原則です。悪い例と直した例を比べます。

// ── 悪い例: 表示に使う値を untracked にしてしまった ──
// total = computed(() => {
//   const price = untracked(() => this.price()); // ×
//   return price * this.quantity();
// });
// → price を変えても合計が画面に反映されない!

// ── 直した例: 計算に使う値は普通に読む ──
// total = computed(() => this.price() * this.quantity());

// 原則: untracked は「ログ・計測・通知」など、
// 画面の表示に関わらない副作用の参考情報にだけ使う