RxJSに基づくAngularのリアクティブ拡張に焦点を当てていると自身が記述しているプラットフォームであるNgRxは、ローカル状態管理用の新しいコンポーネントストアパッケージを備えた10回目のメジャーイテレーションを出荷した。新しいパッケージは、アプリケーション全体の状態管理に使用されるNgRx Storeを補完する。さらに、新しい実験的なNgRxコンポーネントパッケージは、Angular Ivy機能を活用してパフォーマンスを向上させるAngular Zonelessアプリケーションの作成において開発者をサポートするよう努めている。
Brandon Roberts氏は、NgRxのリリースノートで、新しいローカルコンポーネントストアによって追加された価値について説明した:
NgRx Storeは、グローバルアプリケーションレベルでさまざまなソースからの複雑な状態を管理することを目的としています。場合によっては、ローカルで一貫した方法で状態を管理する必要があり、アクション、レデューサー、およびエフェクトの間接化は必要ありません。NgRx Storeと同様のメリットを提供しながら、ローカルレベルで状態を処理する新しいパッケージを設計しました。
NgRxアプリケーションは、ReduxまたはElmアーキテクチャを彷彿とさせる4つの重要な概念に基づいて設計されている。Actionは、ユーザから発信されたものでも、他のインターフェースシステム (ソケットやRESTサーバなど) から発信されたものであっても、イベントをキャプチャする:
import { createAction, props } from '@ngrx/store';
export const login = createAction(
'[Login Page] Login',
props<{ username: string; password: string }>()
);
イベントハンドラは通常、ユーザがトリガするアクションをディスパッチする (前のコードサンプルのlogin
) :
onSubmit(username: string, password: string) {
store.dispatch(login({ username: username, password: password }));
}
アプリケーション全体のNgRx Storeは、そのコンテンツ (つまり、アプリケーション全体の状態) を更新したり、NgRx効果をスケジュールしたりすることで、ディスパッチされたアクションを処理する。この動作は、Elmプラットフォームと言語で普及しているリアクティブシステムの基本方程式と一致している。
トップレベルの状態コンテナがあると、開発者は、すべての状態の更新をトリガイベントまでさかのぼることができる、保守可能で明示的なアプリケーションを作成するのに役立つ。これは実際のところ、Reduxの約束である。
ただし、多くの場合、特にスコープがかなり制限された多数の小さなコンポーネントを備えたアプリケーションでは、開発者は特定の状態を読み取って更新する唯一のコンポーネントを使用している。その状態は通常、特定のコンポーネントのライフサイクルに関連付けられており、そのコンポーネントが破棄されると削除される。したがって、新しいNgRxコンポーネントストアを使用すると、開発者はそのような状態の一部、つまりローカル状態をローカルで処理するコンポーネントを作成できる。
リリースノートの説明:
ComponentStoreは、ローカル/コンポーネントの状態を管理するのに役立つスタンドアロンライブラリです。 これは、リアクティブプッシュベースの「サブジェクトを使用したサービス」アプローチの代替手段です。
コンポーネントストアは、ストアを定義するときに直接初期化できる:
export interface MoviesState {
movies: Movie[];
}
@Injectable()
export class MoviesStore extends ComponentStore<MoviesState> {
constructor() {
super({movies: []});
}
}
コンポーネントストアは、コンポーネントの作成時にsetState
メソッドを使用して初期化することもできる:
@Component({
template: `
<li *ngFor="let movie of (movies$ | async)">
{{ movie.name }}
</li>
`,
providers: [ComponentStore],
})
export class MoviesPageComponent {
readonly movies$ = this.componentStore.state$.pipe(
map(state => state.movies),
);
constructor(
private readonly componentStore: ComponentStore<{movies: Movie[]}>
) {}
ngOnInit() {
this.componentStore.setState({movies: []});
}
}
コンポーネントストアは、関数型レンズと同様の方法で機能する、ローカル状態の一部を読み取るためのselect
メソッドとupdater
メソッドを提供する。setState
メソッドは、ローカル状態全体を更新および置換するためにも公開される。最後に、観察可能なパイプライン内で副作用を実行するためのeffect
メソッドを使用できる。
@Injectable()
export class MoviesStore extends ComponentStore<MoviesState> {
constructor(private readonly moviesService: MoviesService) {
super({movies: []});
}
// Each new call of getMovie(id) pushed that id into movieId$ stream.
readonly getMovie = this.effect((movieId$: Observable<string>) => {
return movieId$.pipe(
// Handle race condition with the proper choice of the flattening operator.
switchMap((id) => this.moviesService.fetchMovie(id).pipe(
// Act on the result within inner pipe.
tap({
next: (movie) => this.addMovie(movie),
error: (e) => this.logError(e),
}),
// Handle potential error within inner pipe.
catchError(() => EMPTY),
))
);
})
readonly addMovie = this.updater((state, movie: Movie) => ({
movies: [...state.movies, movie],
}));
selectMovie(movieId: string) {
return this.select((state) => state.movies.find(m => m.id === movieId));
}
}
新しいコンポーネントストアはnpm
でインストールできる:
npm install @ngrx/component-store --save
または、Angular CLI:
ng add @ngrx/component-store@latest
NgRx 10には、実験的なNgRxコンポーネントパッケージも付属している。リリースノートの説明:
Ivyを使用してこの新しい世界を前進させるとき、Angular Zonelessアプリケーションでのリアクティブの処理方法を再考したいと思います。非同期パイプは、テンプレート内のObservableとPromiseを処理するためのAngularアプリケーションの標準を設定します。NgRxコンポーネントはさらに、テンプレート内のObservableを使用してレンダリングするためのヘルパを使用して、完全にリアクティブなアプリケーションを構築するのに役立ちます。NgRxコンポーネントは、リアクティブなAngularアプリケーションを構築するための新しい方法を検討するための道を私たちにもたらします。
実験パッケージには、別々に文書化されている2つのディレクティブ*ngrxLet
と*ngrxPush
が付属している。
Angular Zonelessアプリケーションは、Angular Zonesメカニズムに頼らずに変更検出を処理しているため、ランタイムパフォーマンスが向上する可能性がある。新しい可能性は、Angular Ivyと事前コンパイルによって開かれる。
NgRxは、MITライセンスの下で配布されているオープンソースソフトウェアである。貢献は大歓迎であり、貢献ガイドラインに従う必要がある。