Angular の最新バージョン以降、新しい基本的な反応性システムがフレームワーク内で開発されました: シグナル!
今日、後から考えると、特定のユースケースがカバーされていなかったことに気づきました。当然、非常に積極的な Angular チームが、これらのユースケースをカバーするためのヘルパーを提供してくれるでしょう。
これらの使用例は何ですか?どのようなソリューションが導入され、どのように使用されるのでしょうか?
この問題を説明することから始めましょう。
一定量の果物が入ったバスケットがあると想像してみましょう。
数量は果物を入力するコンポーネントによって管理されます。
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); count = signal(1); updateQuantity(): void { this.count.update(prevCount => prevCount++); } }
入力価格フルーツが変化した場合、ここで変数をリセットする必要があります。
簡単な解決策はエフェクトを使用することです
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); quantity = signal(1); countEffect(() => { this.fruit(); this.quantity.set(1); }, { allowSignalWrites: true }) updateQuantity(): void { this.quantity.update(prevCount => prevCount++); } }
上記のコードは悪い習慣です。なぜこれが大きな疑問なのでしょうか?
シグナル量を設定するには、signalWrites オプションを true に設定する必要があります。これは、与えられた問題の誤解が原因です。
私たちの場合、実体化では非同期化されている 2 つの変数を同期したいと考えています
カウンターは、最初のソースであるフルーツから独立していません。実際には、ここにはコンポーネント状態があり、その最初のソースはフルーツであり、残りはフルーツの派生です。
問題を次のように具体化します
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{fruitState().quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); fruitState = computed(() => ({ source: fruit(), quantity: signal(1), })); updateQuantity(): void { this.fruitState().quantity.update(prevCount => prevCount++); } }
この物質化は、果物とその量とを強く結びつけます。
したがって、フルーツが変化するとすぐに、計算変数 FruitState が自動的に再計算されます。この再計算では、数量プロパティを持つオブジェクトが返されます。これは、1 に初期化された信号です。
シグナルを返すことで、クリック時に変数をインクリメントし、フルーツが変化したときに単純にリセットできます。
設定は比較的簡単なパターンですが、もっと簡素化できないでしょうか?
Angular 19 の登場により、派生信号を計算するための新しい関数が追加されました。
これまでは計算関数がありましたが、この関数は WrittableSignal ではなく Signal を返します。これは量変数の以前の使用例では実用的でした。
ここで LinkedSignal が登場します。LinkedSignal を使用すると、その名前が示すように、2 つの信号を強力にリンクできます。
前のケースに戻ると、この関数を使用するとコードを次のように簡略化できます。
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); quantity = linkedSignal({ source: fruit, computation: () => 1 }); updateQuantity(): void { this.quantity.update(prevCount => prevCount++); } }
linkedSignal 関数は次のように定義されます:
linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>; linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;
最初の定義 (「省略された」定義) では、linkedSignal 関数は計算関数をパラメーターおよび構成オブジェクトとして受け取ります。
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); count = signal(1); updateQuantity(): void { this.count.update(prevCount => prevCount++); } }
この前の例では、計算関数は信号の量に依存するため、量が変化すると計算関数が再評価されます。
2 番目の定義では、linkedFunction メソッドは 3 つのプロパティを持つパラメーターとしてオブジェクトを受け取ります
「省略された」計算関数とは対照的に、ここでの計算関数はソースの値と「前例」をパラメーターとして受け取ります。
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); quantity = signal(1); countEffect(() => { this.fruit(); this.quantity.set(1); }, { allowSignalWrites: true }) updateQuantity(): void { this.quantity.update(prevCount => prevCount++); } }
Angular 19 では、単純なデータのフェッチと、クエリ ステータス (保留中など)、データ、エラーの取得のための新しい API が導入されます。
フレームワークに少し慣れている人にとって、この新しい API は useRessource フックと少し似た働きをします。
例を見てみましょう:
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{fruitState().quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); fruitState = computed(() => ({ source: fruit(), quantity: signal(1), })); updateQuantity(): void { this.fruitState().quantity.update(prevCount => prevCount++); } }
このコード スニペットについては、知っておくべきことがいくつかあります
このスニペット コードには注意すべき点がいくつかあります:
次の効果はそれらの値を出力します
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); quantity = linkedSignal({ source: fruit, computation: () => 1 }); updateQuantity(): void { this.quantity.update(prevCount => prevCount++); } }
上で説明したように、デフォルトでは、fruitId シグナルは追跡されません。
では、このシグナルの値が変化するたびに http リクエストを再開するにはどうすればよいでしょうか。また、fruitId シグナルの値が変化し、前のリクエストへの応答が変化しなかった場合に、前のリクエストをキャンセルするにはどうすればよいでしょうか。到着しますか?
リソース関数は、request と呼ばれる別のプロパティを受け取ります。
このプロパティは、信号に依存する関数を値として受け取り、その値を返します。
linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>; linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;
上記のコードに示すように、ローダー関数は 2 つのパラメーターを取ります
果物の詳細を取得する httpRequest 中に FruitId シグナルの値が変更された場合、リクエストはキャンセルされて新しいリクエストが開始されます。
最後に、Angular は、この新しい API を RxJ と組み合わせて、Rx オペレーターのパワーを活用できるようにする可能性も考えました。
相互互換性は、resource 関数とまったく同じ方法で定義される rxResource 関数を使用して実現されます。
唯一の違いは、observable
を返すローダー プロパティの戻り値の型です。
@Component({ template: `<button type="button" (click)="updateQuantity()"> {{quantity()}} </button>` }) export class QuantityComponent() { fruit = input.required<string>(); count = signal(1); updateQuantity(): void { this.count.update(prevCount => prevCount++); } }
ここでは、abortSignal を使用する必要はありません。これは、シグナル FruitId の変更の値が関数 rxResource で暗黙的に変更され、動作が switchMap オペレーターと同じになる場合に、前のリクエストをキャンセルすることを意味します。
以上がAngular の反応性の次の改善の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。