Edited at

キャッシュするHTTPクライアントをBehaviorSubjectを使って実装してみた例


:point_right: 今回の構成

Angular4を使ってます。


:point_right: 今回の経緯

同じページで複数の子コンポーネントから同じリクエストを投げていたのを1回のリクエストで済ませたかった。


:point_right: やったこと

HttpClientをもったサービスを作り、Getリクエストに対してBehaviorSubjectをObservableにして返すメソッドをはやした。


:point_right: やってないこと

GETパラメータ対応とかやってない...

BehaviorSubjectの初期値を決めなきゃならなかったので配列限定にした...

とまあ実際にいろいろアプリに組み込む上であとで掲載するコードよりはもう少しファットになっているのですが、BehaviorSubject便利!の一つとしてシンプルに伝わればいいかなと思い省いています。


Subjectとは?

nextメソッドで流れてきた値をSubscriberに流す、SubscriberでありObservableなやつ

const subject = new Subject<number>();

subject.subscribe(number => console.log(number));
subject.next(1);
//出力: 1


BehaiviorSubjectとは?

Subjectに前回の値を保持する機能をもったもの。

.subscribeされたときに前回保持した値を最初に流す。

const behaiviorSubject = new BehaiviorSubject<number>(1); //Subjectと違い初期値をいれる必要あり

subject.subscribe(number => console.log(number)); //初期値が流れてくる
//出力: 1
subject.next(2);
//出力: 2
subject.subscribe(number => console.log(number));
//出力: 2


HTTPリクエストに使ってレスポンスを保持したもの

リクエストパスをキーに リクエストパス: BehaiviorSubject のMapを持っている。

getメソッド呼び出し時に

MapになければHTTPリクエスト、

MapにあればBehaiviorSubjectをObservableにして返す。

といったことをしております。

export class CachedApiService {

private requestMap: Map<string, BehaviorSubject<any[]>> = new Map();

constructor(private http: HttpClient) {}

get<T>(path: string) {
if (!this.requestMap.get(path)) {
this.requestMap.set(path, new BehaviorSubject<T[]>([]));

this.http.get<T[]>(path, { params: this.params }).subscribe(
response => {
this.requestMap.get(path).next(response);
},
error => this.requestMap.get(path).error(error)
);
}

return this.requestMap.get(path).asObservable() as Observable<T[]>;
}
}


Angularでよかったこと

キャッシュ系っていつ更新するのか、とかどこに保持しておくのとか考える&管理するのが面倒なイメージだったのですが、AngularのServiceとして作ることで表示する一番親ページのproviderとして呼び出せば同じページ内だけ重複したリクエストが飛ばない仕様にでき楽でした。

以下呼び出すとき

@Component({

selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
providers: [ CachedApiService ] // ← ここ
})
export class ParentComponent implements OnInit {
...

簡単ですが以上です!!