概要
クライアントからのAPIコールは可能であれば避けた方がサーバの負荷も下がり、ユーザの体感速度も上がります。
1度取得すればほぼ変わらないデータなどは、最初にAPIコールした後はできれば避けたいです。
一方でcookieやlocalstorageで管理するほどでもない、というときはBehaviorSubjectを利用します。
環境
- Angular 4.3.4
- rxjs 5.4.2
BehaviorSubjectとは
基本的な動作
BehaviorSubjectの大きな特徴は直前にonNext
で渡された値を保持し、subscribe()
やgetValue()
するとその保持していた値を取得できるところです。
ref: ReactiveX - Subject
サンプル
const sub = new Rx.BehaviorSubject(1); sub.subscribe(v => { console.log("1st: " + v); }); console.log('getValue1: ' + sub.getValue()); console.log('next: 10'); sub.next(10); console.log('getValue2: ' + sub.getValue()); sub.subscribe(v => { console.log("2nd: " + v); });
結果
"1st: 1" "getValue1: 1" "next: 10" "1st: 10" "getValue1: 10" "2nd: 10"
エラー時の動作
エラーが起きた時は、それ以前にsubscribe
していれば値を取得できますが、エラー後にsubscribe
すると値は取得できずエラーのみ取得できます。
ref: ReactiveX - Subject
サンプル
const sub = new Rx.BehaviorSubject(1); sub.subscribe(v => { console.log("1st: " + v); }, err => { console.log("1st error: " + err); }); console.log('next: 10'); sub.next(10); const error = new Error("some error"); sub.error(error); sub.subscribe(v => { console.log("2nd: " + v); }, err => { console.log("2nd error: " + err); });
結果
"1st: 1" "next: 10" "1st: 10" "1st error: Error: some error" "2nd error: Error: some error"
APIコールのキャッシュとしての使い方
以下のように役割を分けます。
メソッド | 役割 |
---|---|
fetch() | APIコールしてデータを取得。取得後にSubjectにデータを追加 |
get() | キャッシュがあればそれを利用。無ければfetch() に委譲 |
コードとしては以下のようになります。
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import 'rxjs/add/operator/do'; import { Article } from './article'; @Injectable() export class ArticleService { private articleSource: BehaviorSubject<Article> = new BehaviorSubject<Article>(new Article()); constructor(private http: HttpClient) { } get(id: string): Observable<Article> { const article = this.articleSource.getValue(); if (Object.keys(article).length !== 0) { return this.articleSource; } return this.fetch(id); } fetch(id: string): Observable<Article> { return this.http.get<Article>('http://jsonplaceholder.typicode.com/posts/' + id) .do(article => { this.articleSource.next(article); }); } }
まとめ
BehaviorSubjectを使うことで、リロードなどで内部オブジェクトがリセットされない限りはずっと保持できるようになり、無駄なAPIコールを減らすことができます。
Updateなどの時もfetch()
の時と同様にnext()
でデータを更新することで、get()
時にデータがちゃんと更新されていきます。