Marginalia

Angular: Signal Inputsで何が変わるのか

Angular v17.1.0にて、Signal Inputsという機能がリリースされる見込みだ。Signals APIのRFC段階から提案されていたものだが、ついに実際に使えるAPIになる。この記事ではSignal Inputsによって何が変わるのかをかいつまんで解説する。

Signal Inputsとは

Signal InputsはSignals RFCの中でもSignal-based Componentsのセクションで提案されている。

In signal-based components, all component inputs are signals.

ここで書かれているように、コンポーネントのインプットプロパティをSignalとして宣言できる。従来のSignalではないオブジェクトでは、インプットの変更に反応して処理をおこなうために ngOnChanges ライフサイクルフックや、setterによる代入処理への割り込みが必要だったが、Signal Inputsでは通常のSignalと同様にcomputedeffectによってリアクティブな処理ができる。

以下は実際に動作するサンプルコードだ。input() によってインプットプロパティを宣言している。親コンポーネントから値を必ず受け取りたい場合はinput.required()で指定できる。コンポーネント内ではSignalとして扱えるため、インプットの値が変わったときに行いたい処理はeffectを使う。

Signal Inputsで何が変わるのか

Signal Inputsへの移行により、特に以下の3つの点が大きく変わると考えられる。

ngOnChangesが不要になる

これまではどのインプットが更新されても単一のngOnChangesライフサイクルフックで待ち受けるか、あるいはsetterを使うしかなかったが、それらはSignal Inputsとeffectで一切不要になる。setterで入力された値の前処理をしていたようなケースも、今はすでにInput Transformもあるため、インプットプロパティのsetterが必要なケースは皆無だろう。

TypeScriptのstrictPropertyInitializationを常に有効化できる

TypeScriptのstrictモードに含まれているstrictPropertyInitialization は、クラスプロパティが必ず初期化されていることをチェックする設定だが、親から値を受け取ることを前提にしたコンポーネントではインプットプロパティに初期値を入れられず、strictPropertyInitialization フラグをオフにしなければならないケースがあった。このことについては以前に記事を書いている。

Signal InputsによってすべてのインプットプロパティはSignal型のオブジェクトで初期化されることになるため、この記事で書いたベストプラクティスはすべて過去のものとなる。strictPropertyInitializationは常に有効化できるようになるはずだ。

インプットがイミュータブルになる

input()によって作成されるInputSignalオブジェクトは、computedと同じく読み取り専用のSignalである。これまではプリミティブ型ではないミュータブルなオブジェクトがインプットプロパティに渡された場合、子コンポーネントから親コンポーネントへ変更が逆流することがありえた。Signal Inputsであればそのようなことはなくなり、バグが起きにくくなる。

参考リンク