LoginSignup
8
6

More than 5 years have passed since last update.

【RxJS】Subscribeの中で無駄な処理をさせないためには

Last updated at Posted at 2017-11-30

はじめに

Observablesubscribeして、その中でAPIの呼び出しなどを行う処理を書くことがあるかと思う。自分の実装方法が悪いのかもしれないが、無駄にAPIが呼びされてしまっているケースがよく見受けられる。console.log()で出力すると、エグいことになっていることもある…

hoge$.subscribe((hoge: Hoge) => {
  this.hogeService.callApi(hoge.fuga);
});

何がしたいのか

パフォーマンスの低下にも繋がるし、やはり無駄な処理は極力実行されないようにしたい。特にやりたいのが、「現在の状態」と「過去や未来の状態」を比較した上で、必要な時にだけ処理が実行されるようにしたい

※Reactでは、当たり前のようにそのような機能が備わっていた。


Reactの場合


reactの場合1: setState()

コンポーネントのstateを更新し、ビューを更新するためのメソッド。

setState((prevState, props) => {

  // prevState.hoge === props.hoge 比較可能

});


reactの場合2: Lifecycleメソッド

Lifecycleメソッドも考慮済み。以下は、ライフサイクルメソッドのひとつで、コンポーネントがアップデートされる直前で呼び出されるcomponentWillUpdate()。

componentWillUpdate(nextProps, nextState) {

  // nextProps.hoge === this.props.hoge 比較可能
  // nextState.hoge === this.state.hoge 比較可能

};


reactの場合3: shouldComponentUpdate()

その名も「コンポーネントをアップデートすべき」。falseを返せば、コンポーネントの更新を停止できる。

shouldComponentUpdate(nextProps, nextState) {

  // nextProps.hoge === this.props.hoge 比較可能
  // nextState.hoge === this.state.hoge 比較可能

}


Angularの場合


Angularの場合はどうする?

Lifecycle hooksには、新旧の状態を比較するような機能はなさそう。そもそもRxJS側の話でもある。

ということで、RxJSでReactと同じようなことができないか調べてみた。すると良さそうなRxJSのオペレータがあった。


distinctUntilChanged


distinctUntilChanged

Reactにもあった新旧の状態を比較するためのもの。trueを返せばストリームを止められる。(1回目は自動的に出力)

hoge$
  .pipe(
    distinctUntilChanged((prev, current) => {
      // prev === current 比較可能
    }),
  )
  .subscribe(...);

実装例

import { of } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';

interface Data {
  id: number;
  name: string;
};

of<Data>(
  { id: 1, name: 'hoge' },
  { id: 1, name: 'hoge' },
  { id: 2, name: 'fuga' },
  { id: 2, name: 'fuga' },
)
.pipe(
  distinctUntilChanged((prev: Data, current: Data) => {
    return prev.id === current.id;
  }),
)
.subscribe((data: Data) => this.hogeService.callApi(data.name));

結果: this.hogeService.callApi()の呼び出しは2回


注意

JavaScriptのオブジェクトの比較

以下は別物とみなされる。

{ a: 'a' } === { a: 'a' } // => false

===はメモリアドレスの比較であり、{ a: 'a' }new Object(…) という意味で、それぞれ新しくオブジェクトを生成しており、別のメモリアドレスに保存されるから。


注意(続き)

JavaScriptのオブジェクトの比較

オブジェクトを比較するには、値同士を比較したり、文字列に変換したりするなど方法は色々考えられる。

const aaa = { a: 'a' }
aaa.a === aaa.a // => true

JSON.stringify({ a: 'a' }) === JSON.stringify({ a: 'a' }) // => true

その他に使えそうなもの1

distinctオペレータ

前後関係なく、比較する対象に重複したものがあれば、すべてtrueとなり、処理を停止できる。

distinctUntilKeyChangedオペレータ

distinctUntilChangedと同様、新旧の状態を比較するが、比較対象としてkeyを指定できる。


その他に使えそうなもの2

switchMapオペレータ

受け取った値から別のObservableを生成し、元々のObservableに格納しつつ、元々のストリームを破棄する。

pairwiseオペレータ

distinctUntilChangedと同じようなもの。こちらは新旧の状態を配列で受け取る。


まとめ

これが正解かどうかはわからない。他に良い方法があれば教えてください。

Angularを始めてから約2ヶ月。特にRxJSは奥が深くて、わかっているようでまだよくわかっていない部分が多々ある。RxJSを使いこなせるようになるには、いろいろハマってもっともっと試行錯誤していく必要があると思う今日この頃。

8
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6