first()
と filter()
の使い方注意
特定のイベントを受けたときに BehaviorSubject を覗いて、条件が成り立っていたらなにか処理を行う、という実装をするときにこのように実装することがある。
function onSomeEvent() {
someSubject$.pipe(
first(),
filter((someValue) => someValue.isHoge()),
).subscribe((someValue) => {
// Process someValue
});
これは問題ないのだが、よく first()
と filter()
の順序を間違える事がある。
function onSomeEvent() {
someSubject$.pipe(
filter((someValue) => someValue.isHoge()),
first(),
).subscribe((someValue) => {
// Process someValue
})
}
この場合、動くは動くが、someValue.isHoge()
が偽の場合でも someSubject$
が開いたままになっているので、メモリリークの原因になってしまう。
必ず最初に first()
を入れて、確実に complete 状態になるようにしよう。
また、うっかり first()
を入れ忘れるのもよくある。
function onSomeEvent() {
someSubject$.pipe(
filter((someValue) => someValue.isHoge()),
).subscribe((someValue) => {
// Process someValue
})
}
この場合も、二重に動くので動作確認で異常なしに見えるし、単体テストもそのテスト自体は通ってしまうことがあるが、.unsubscribe()
されていないので
その次のテストで落ちることになる。
いずれの場合も原因が非常に分かりづらいので、注意されたし。
安全なプラクティスとしては、filter()
を使わずに first()
に条件式を渡してしまうことである。
function onSomeEvent() {
someSubject$.pipe(
first((someValue) => someValue.isHoge()),
).subscribe((someValue) => {
// Process someValue
})
}
これであれば、 filter()
との順序や入れ忘れで悩むことは少なくなる。
別解としては、 .subscribe()
の中でif文で判定してしまう方法もある。
シンプルなケースによってはこれでも良い。
function onSomeEvent() {
someSubject$.pipe(
first(),
).subscribe((someValue) => {
if (!someValue.isHoge()) {
return;
}
// Process someValue
})
}
first()
の場合と同じく、 take()
も気をつける必要がある。
こちらは条件式を取らないので、順序には気をつけよう。