RxJSを使用してReactアプリケーションの状態を管理する実践的な共有


フロントエンドアプリケーションの複雑さが高まるにつれて、アプリケーションのデータをどのように管理するかは避けられない問題である.あなたが直面しているのは
ビジネスシーンが複雑で、需要の変動が頻繁で、さまざまなアプリケーションデータが相互に関連して依存する大規模なフロントエンドアプリケーションでは、アプリケーションのステータスデータをどのように管理しますか?
アプリケーションのデータは大きく4つに分類できると考えられています.
  • 事件:瞬時に発生したデータは、データが消費された直後に破棄され、格納されない.
  • 非同期:非同期で取得したデータ;イベントに似ており、瞬時データであり、格納されません.
  • ステータス:時間空間が変化するデータは、常に現在の値/最新の値を格納します.
  • 定数:不変のデータを固定します.
  • RxJSは生まれながらにして非同期とイベントベースのプログラムを書くのに適しているが、状態データは何で管理されているのだろうか.それともRxJSを使いますか?合いますか?
    フロントエンドコミュニティの既存の優れた状態管理ソリューションを調査し、学習し、RxJSでデータ層を設計する構想と実践から啓発されました.
  • は、RxJSを使用して、ReduxMobxなどの状態データを管理する機能を完全に実現することができる.
  • アプリケーションのデータは、ステータスのみでなく、イベント、非同期、定数などもあります.アプリケーション全体がobservableによって表現される場合、RxJSによってシーケンスベースで応答可能な特性を利用して、様々なタイプのデータをストリームのように自由につなぎ合わせ、組み合わせることができ、多重化可能で拡張可能なビジネスモデルをより優雅かつ効率的に抽象化することができる.

  • 以上の2つの理由から、RxJSに基づいて管理アプリケーションの状態を設計するソリューションを最終的に決定した.

    原理の紹介


    ステータスの定義では、通常、ステータスは次の3つの条件を満たす必要があります.
  • は、複数の値を有する集合である.
  • は、eventまたはactionによって値を変換し、新しい値を得ることができる.
  • には「現在値」という概念があり、一般的には現在値、すなわち最新値のみが露出される.

  • では、RxJSはステータスデータの管理に適していますか?答えは肯定的だ!
    まず、Observable自体が複数の値のプッシュ集合なので、1つ目の条件は満たされています!
    次に、dispatch actionモードを用いてデータをプッシュするobservableを実現して、第2の条件を満たすことができる.
    周知のように、RxJSobservableは、2つのタイプに分けることができる.
  • cold observable:値をプッシュする生産者(producer)は、observableの内部から来ている.
  • は、いくつかの値をプッシュし、どのような値をプッシュするかをobservableが作成されたときに定義し、変更できません.
  • producerは、観察者(observer)と一対一の関係、すなわち、ユニキャストである.
  • は、observerが購読するたびに、producerが予め定義されたいくつかの値をobserverに順次プッシュする.

  • hot observable:プッシュ値のproducerobservableの外部から来ている.
  • では、いくつかの値がプッシュされ、どのような値がプッシュされるか、作成時にプッシュされるタイミングは不明です.
  • producerobserverは、一対多の関係、すなわちマルチキャストである.
  • は、observerが購読されるたびに、他のライブラリまたは言語のobserverと同様に、addListenerを観察者リストに登録する.
  • 外部のproducerがトリガまたは実行されると、値はすべてのobserverに同時にプッシュされる.すなわち、observerのすべての値は、hot observableによってプッシュされた値を共有する.

  • RxJSが提供するBehaviorSubjectは、プッシュデータのインタフェースhot observable関数を外部に露出する特殊なnextである.また、observerに送信された最新値を保存する「現在値」という概念があり、新しいオブザーバーが購読すると、直ちにBehaviorSubjectから「現在値」が受信されます.
    これは、BehaviorSubjectを用いて状態を更新し、保存する現在の値が実行可能であり、第3の条件も満たされていることを示している.

    単純な実装


    次のコードを参照してください.
    import { BehaviorSubject } from 'rxjs';
    
    //         
    class StateMachine {
      constructor(subject, value) {
        this.subject = subject;
        this.value = value;
      }
    
      producer(action) {
        let oldValue = this.value;
        let newValue;
        switch (action.type) {
          case 'plus':
            newValue = ++oldValue;
            this.value = newValue;
            this.subject.next(newValue);
            break;
          case 'toDouble':
            newValue = oldValue * 2;
            this.value = newValue;
            this.subject.next(newValue);
            break;
        }
      }
    }
    
    const value = 1;  //       
    const count$ = new BehaviorSubject(value);
    const stateMachine = new StateMachine(count$, value);
    
    //   action
    function dispatch(action) {
      stateMachine.producer(action);
    }
    
    count$.subscribe(val => {
      console.log(val);
    });
    
    setTimeout(() => {
      dispatch({
        type: "plus"
      });
    }, 1000);
    
    setTimeout(() => {
      dispatch({
        type: "toDouble"
      });
    }, 2000);
    

    コードコンソールを実行すると、次の3つの値が印刷されます.
    Console
    
     1
     2
     4

    上記のコードは、単純な管理状態の例を簡単に実現しています.
  • 状態の初期値:1
  • 実行plus以降の状態値:2
  • 実行toDouble以降の状態値:4
  • 実装方法は、BehaviorSubjectを使用して状態の現在の値を表現することです.
  • の第1のステップは、dispatch関数を呼び出すことによってproducer関数に
  • を実行させる.
  • 第2部,producer関数は内部でBehaviorSubjectnext関数を呼び出し,新しいデータをプッシュし,BehaviorSubjectの現在値が更新され,すなわち状態が更新された.

  • しかし、書くのは少し煩雑で、私たちはそれをパッケージして、最適化した後の書き方は以下の通りです.

    オペレータを使用してステータスデータを作成する


    オペレータstateをカスタマイズして、dispatch actionモードで新しいデータをプッシュできるBehaviorSubjectを作成しました.これをstateObservableと呼びます.
    const count$ = state({
      //          
      name: "count",
        
      //       
      defaultValue: 1,
        
      //           
      producer(next, value, action) {
        switch (action.type) {
          case "plus":
            next(value + 1);
            break;
          case "toDouble":
            next(value * 2);
            break;
        }
      }
    });

    ステータスの更新


    任意の場所で関数dispatchを使用してactionを派遣すると、ステータスが更新されます.
    dispatch("count", {
      type: "plus"
    })

    非同期データ

    RxJSの大きな利点は、同期と非同期を統一できることです.observableを使用してデータを処理するには、同期か非同期かに注目する必要はありません.
    次の例では、オペレータfromを使用してpromiseobservableに変換します.

    ステータスの初期値としてobservableを指定します(データを最初にプッシュ)

    const todos$ = state({
      name: "todos",
        
      // `observable`              
      initial: from(getAsyncData())
        
      //...
      
    });

    producerプッシュobservable

    const todos$ = state({
      name: "todos",
        
      defaultValue: []
        
      //           
      producer(next, value, action) {
        switch (action.type) {
          case "getAsyncData":
            next(
              from(getAsyncData())
            );
            break;
        }
      }
    });
    getAsyncDataが実行されると、from(getAsyncData())のプッシュデータが状態の最新値となる.

    派生ステータス


    ステータスtodos$は1つのobservableであるので、RxJSオペレータを使用して別の新しいobservableに変換することは自然に可能である.このobservableのプッシュはtodos$から来ている.つまり、todos$が新しいデータをプッシュすれば、それもプッシュされます.効果は、Vueの計算プロパティに似ています.
    //        
    const undoneCount$ = todos$.pipe(
      map(todos => {
        let _conut = 0;
        todos.forEach(item => {
          if (!item.check) ++_conut;
        });
        return _conut;
      })
    );

    Reactビューレンダリング


    コンポーネントのライフサイクル内でobservableを購読してデータレンダリングビューを得ることができます.
    class Todos extends React.Component {
      componentWillMount() {
        todos$.subscribe(data => {
          this.setState({
            todos: data
          });
        });
      }
    }

    さらに最適化することができ、高次コンポーネントを用いて装飾関数@subscriptionをカプセル化することができ、その名の通り、Reactコンポーネントのためにobservableを購読し、プッシュデータの変化に応答することである.observableによってプッシュされたデータは、Reactコンポーネントのpropsに変換されます.
    @subscription({
      todos: todos$
    })
    class TodoList extends React.Component {
      render() {
        return (
          

    タスク・リスト

    {this.props.todos.map((item, n) => { return ; })}
    ); } }

    まとめ

    RxJSを長く使うほど、利益が得られます.
  • は、observableシーケンスに基づいてより高いレベルの抽象化を提供し、観察者モードであるため、各コンポーネントの各モジュール間の結合度を可能な限り低減することができ、位置決めBUGと再構成の負担を大幅に軽減することができる.
  • observableシーケンスに基づいてコードを記述するため、複雑なビジネスシーンに遭遇し、observableを一定の順序で使用して記述することができ、コードの可読性が強い.そして、需要が変動した場合、observableの順序を調整するか、オペレータを追加すればいいかもしれません.複雑な業務プロセスで変更する必要はありません.いくつかの場所のコードを変更する必要があります(しかもBUGを変更しやすく、笑~).

  • したがって、以上のRxJSに基づくステータス管理スキームは、私たちのプロジェクトでRxJSが大量に使用されているため、ステータスデータもobservableであれば、抽象的な多重化可能な拡張性のあるビジネスモデルに非常に大きな助けになります.もちろん、あなたのプロジェクトでRxJSを使用していない場合は、ReduxMobxがより適切な選択かもしれません.
    このRxJSに基づく状態管理案は、開発会社のビジネスプロジェクトに使用されており、フィードバックは悪くありません.そこで私たちはこの案をjs libに整理し、Flowayと名付け、githubでオープンソースを開くことにしました.
  • githubソース:https://github.com/shayeLee/floway
  • 文書の使用:https://shayelee.github.io/floway
  • starを歓迎して、更にみんなが共同でRxJSの使用の心得を交流して分かち合うことを歓迎します!参考記事:
  • 複雑な単一ページアプリケーションのデータ層設計
  • DaoCloud RxJSベースのフロントエンドデータ層実践