みなさんこんにちは、現役エンジニアのサメハックです
未経験からWebエンジニアに転職し、
正社員として5年働いたのちフリーランスとして独立しました。
Angularの解説シリーズです。
今回はBehaviorSubjectについて学んでいきましょう!
駆け出しエンジニアや未経験の方、
また新入社員を指導する先輩社員にとっても
わかりやすいように解説していきます!
この記事を読むと・・・
- BehaviorSubjectを使った状態管理が理解出来る
※PCにnpm、nodeがインストールされている前提で記述します。
yarn等をお使いの方は読み替えてください。
環境がない人はcodesansboxなどを使ってね!
作りたいもの
これが今回の完成形だよ!
実行環境
Angularのバージョンが古いと動かないことがあるよ!
うまく動かなければアップデートしてね!
うまく動かなければアップデートしてね!
BehaviorSubjectを使った状態管理をしよう!
BehaviorSubjectとは
BehaviorSubjectとはAngularのRxJSを使った状態管理システムです。
BehaviorSubjectを使うことで、どのコンポーネントからも
共通の保管された情報へアクセスすることが出来ます。
使い方としては
- BehaviorSubjectに値を保管する
- 保管した値を更新する
- 保管した値を購読する
- 購読を解除する
という処理がセットになります。
BehaviorSubjectを使うことで、
全てのコンポーネントから同じ情報にアクセスできるよ!
全てのコンポーネントから同じ情報にアクセスできるよ!
BehaviorSubjectに値を保管する構文
import { BehaviorSubject } from 'rxjs';
// BehaviorSubjectの宣言
データ保管用変数 = new BehaviorSubject<型>(初期値);
ここに値が保管されるよ!
保管した値を更新する構文
データ保管用変数.next(更新したい値);
保管した値を更新するにはnext()を使うよ!
保管した値を購読する
// 購読した値を格納する変数
ローカル変数X: any;
// 購読設定停止用
private subscriptions = new Subscription();
ngOnInit(): void {
this.subscriptions.add(
// データ保管用変数を購読する
// ※データ保管用変数が更新される度に呼ばれる
データ保管用変数.subscribe(
(response) =>
// ローカル変数Xに購読した値を代入し、情報をリンクさせる
(this.ローカル変数X = response)
)
);
}
Subscriptionにaddしておくことでまとめて購読の解除ができるよ!
購読を解除する
ngOnDestroy(): void {
/**ページ遷移等部品が呼ばれなくなったタイミングで購読を停止 */
this.subscriptions.unsubscribe();
}
ちなみにsubscribeの戻り値はsubscriptionだよ!
保管した値を取得
データ保管用変数.getValue()
実際に作ってみよう!
今回はpage1,page2,page3という3つのコンポーネントから
servieファイルに保管したuser$を参照し、
全てのコンポーネントで同じ値が表示されるアプリを作ります。
今回のコードはGitHubにあげているので、
すぐに動かしたい人は本記事後半からダウンロードしてね!
すぐに動かしたい人は本記事後半からダウンロードしてね!
コンポーネントの作成
ng new my-app
ng generate service service/user
ng generate component page1
ng generate component page2
ng generate component page3
サービスファイルの設定
user.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
// User型
export interface User {
name: String;
age: number;
job: String;
}
@Injectable({
providedIn: 'root',
})
export class UserService {
// 初期値用
data = {
name: 'サメハック',
age: 29,
job: 'エンジニア',
};
// ユーザの持つ値を保管する
user$ = new BehaviorSubject(this.data);
/**user$を更新 */
upadte(userInfo: User) {
console.log('更新前 ', this.user$.getValue());
// this.user$の保管する値を更新
this.user$.next(userInfo);
console.log('更新後 ', this.user$.getValue());
}
/**user$のageをインクリメント */
increment() {
console.log('インクリメント前 ', this.user$.getValue().age);
// user$に保管した値ののageプロパティのみを更新
const userInfo = {
...this.user$.getValue(),
age: this.user$.getValue().age + 1,
};
// this.user$の保管する値を更新
this.user$.next(userInfo);
console.log('インクリメント後 ', this.user$.getValue().age);
}
constructor() {}
}
このサービスファイルで、値の保管と更新を行うよ!
page1.component
<section style="background-color: pink">
<h2>page1</h2>
<p>名前: {{ page1User.name }}</p>
<p>年齢: {{ page1User.age }}</p>
<p>職業: {{ page1User.job }}</p>
<button (click)="updateUserInfo()">ユーザ情報更新</button>
</section>
import { Component, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { UserService, User } from '../service/user.service';
@Component({
selector: 'app-page1',
templateUrl: './page1.component.html',
styleUrls: ['./page1.component.scss'],
})
export class Page1Component implements OnInit {
// サービスファイルの読み込み
constructor(private service: UserService) {}
// 購読設定停止用
private subscriptions = new Subscription();
// page1で表示するユーザ情報
page1User: User;
ngOnInit(): void {
this.subscriptions.add(
// user$を購読する
// ※user$が更新される度に呼ばれる
this.service.user$.subscribe(
(userInfo) =>
// pageUser1に購読したuser$の値を代入し、情報をリンクさせる
(this.page1User = userInfo)
)
);
}
/**更新したいユーザ情報をサービスに渡す */
updateUserInfo() {
const newUser: User = {
name: 'ねこハック',
age: 24,
job: 'OL',
};
// user$を更新
this.service.upadte(newUser);
}
ngOnDestroy(): void {
/**ページ遷移時に購読を停止 */
this.subscriptions.unsubscribe();
}
}
ユーザの情報を更新するコンポーネントだよ!
page2.component
<section style="background-color: skyblue">
<h2>page2</h2>
<p>名前: {{ page2User.name }}</p>
<p>年齢: {{ page2User.age }}</p>
<p>職業: {{ page2User.job }}</p>
<button (click)="clearUserInfo()">ユーザ情報クリア</button>
</section>
import { Component, OnInit } from '@angular/core';
import { UserService, User } from '../service/user.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-page2',
templateUrl: './page2.component.html',
styleUrls: ['./page2.component.scss'],
})
export class Page2Component implements OnInit {
// サービスファイルの読み込み
constructor(private service: UserService) {}
// 購読設定停止用
private subscriptions = new Subscription();
// page2で表示するユーザ情報
page2User: User;
ngOnInit(): void {
this.subscriptions.add(
// user$を購読する
// ※user$が更新される度に呼ばれる
this.service.user$.subscribe(
(userInfo) =>
// pageUser2に購読したuser$の値を代入し、情報をリンクさせる
(this.page2User = userInfo)
)
);
}
/**空のユーザ情報をサービスに渡す */
clearUserInfo() {
const newUser: User = {
name: '',
age: 0,
job: '',
};
// user$を更新
this.service.upadte(newUser);
}
ngOnDestroy(): void {
/**ページ遷移時に購読を停止 */
this.subscriptions.unsubscribe();
}
}
ユーザ情報をクリアするコンポーネントだよ!
page3.component
<section style="background-color: lightgreen">
<h2>page3</h2>
<p>名前: {{ page3User.name }}</p>
<p>年齢: {{ page3User.age }}</p>
<p>職業: {{ page3User.job }}</p>
<button (click)="updateUserAge()">年齢を+1</button>
</section>
import { Component, OnInit } from '@angular/core';
import { UserService, User } from '../service/user.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-page3',
templateUrl: './page3.component.html',
styleUrls: ['./page3.component.scss'],
})
export class Page3Component implements OnInit {
// サービスファイルの読み込み
constructor(private service: UserService) {}
// 購読設定停止用
private subscriptions = new Subscription();
// page3で表示するユーザ情報
page3User: User;
ngOnInit(): void {
this.subscriptions.add(
// user$を購読する
// ※user$が更新される度に呼ばれる
this.service.user$.subscribe(
(userInfo) =>
// pageUser3に購読したuser$の値を代入し、情報をリンクさせる
(this.page3User = userInfo)
)
);
}
/**ユーザの年齢をインクリメント */
updateUserAge() {
// user$を更新
this.service.increment();
}
ngOnDestroy(): void {
/**ページ遷移時に購読を停止 */
this.subscriptions.unsubscribe();
}
}
ユーザの年齢を+1するコンポーネントだよ!
npm start
これでnpm startすると、同じものが完成します。
どのコンポーネントを操作しても全て同じデータが表示されるので
BehaviorSubjectで正常に状態管理されていることがわかります。
GitHubのサンプルコード
今回作ったものはGitHubにあげているので
使いたい人は是非ダウンロードしてみてください。
GitHub - same-hack/Angular-BehaviorSubject: AngularのBehaviorSubjectのサンプルコードです。
AngularのBehaviorSubjectのサンプルコードです。. Contribute to same-hack/Angular-BehaviorSubject development by creating an account on GitHub.
まとめ
- BehaviorSubject 値を保管する
- next() 保管した値を更新
- subscribe 保管した値を購読する
- Subscription.unsubscribe 購読を解除する
満足いただけたら、1クリックなのでSNSフォローしてもらえると嬉しいです🦈