web-dev-qa-db-ja.com

WebSocketsを使用した反応ネイティブアプリのアーキテクチャ

React WebSocketを使用してビルドするネイティブアプリがあります。JavaScriptで記述されたWebSocketライブラリがあり、このプロジェクトで単純に再利用しています。これは素晴らしいことです。 。

私の質問は、React/React Nativeが初めてなので、WebSocketを通過するすべてのトラフィックを設定および維持するためのベストプラクティスは何ですか?

最初は、メインのAppコンポーネントに次のようなwebsocketを作成することを考えていました。

export default class App extends Component {

  constructor(props) {
    super(props);
    this.ws = new WebSocket;
  }

  componentWillMount() {
    console.log(this.ws);
  }

  render() {
    console.log("We are rendering the App component.....");

    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Hello, world</Text>  
      </View>
    );
  }
}

実際のWebSocketクラスには、それぞれの接続処理がすべて含まれます。

ws.onopen = () => {
  // connection opened
  ws.send('something'); // send a message
};

ws.onmessage = (e) => {
  // a message was received
  console.log(e.data);
};

ws.onerror = (e) => {
  // an error occurred
  console.log(e.message);
};

ws.onclose = (e) => {
  // connection closed
  console.log(e.code, e.reason);
};

私の質問は、WebSocket経由で送信されるデータはReactネイティブアプリの多くのコンポーネントを通じて状態に適用できるため、React.Componentを拡張するクラスではないので、 WebSocketクラスのReduxと対話しませんか?すべてのWebSocket接続処理をAppコンポーネントに移動し、そこでアクションをReduxにディスパッチしますか?

WebSocketクラスをインスタンス化し、その中のすべてのトラフィックがReduxに適切に渡されるようにして、すべてのコンポーネントの状態を正しく反映させるための一般的なパターンは何ですか?

24
randombits

これまでのところ、素晴らしい答えです。データをに追加したかっただけですデータを保持する(---)は、データのタイプに基づいて決定する必要があります。 James Nelsonは このトピックに関する優れた記事 を私が定期的に参照しています。

あなたの場合のために、状態の最初の3つのタイプについて話しましょう:

  1. データ
  2. 通信状態
  3. 制御状態

データ

WebSocket接続は汎用的であり、技術的には何でも返すことができますが、受信しているメッセージはデータである可能性があります。たとえば、チャットアプリを構築しているとします。次に、送受信されたすべてのメッセージのログがデータになります。このデータは、messagesレデューサーを使用してreduxに保存する必要があります。

_export default function messages(state = [], action) {
    switch (action.type) {
        case 'SEND_MESSAGE': 
        case 'RECEIVE_MESSAGE': {
            return [ ...state, action.message ];
        } 

        default: return state;
    }
}
_

汎用的なものであり、データがどこから来ているかを気にしないので、レデューサーにWebSocketロジックを含める必要はありません(含めるべきではありません)。

また、このレデューサーは送信と受信をまったく同じ方法で処理できることに注意してください。これは、ネットワーク通信が通信状態リデューサーによって個別に処理されるためです。

通信状態

WebSocketを使用しているため、追跡する通信状態のタイプは、私の例とは異なる場合があります。標準APIを使用するアプリでは、リクエストがloadingfailed、またはsuccessful

私たちのチャットアプリの例では、メッセージを送信するたびにこれらのリクエストの状態を追跡したいと思うかもしれませんが、通信状態として分類したいことが他にもある可能性があります。

networkリデューサーはmessagesリデューサーと同じアクションを使用できます。

_export default function network(state = {}, action) {
    switch (action.type) {
        case 'SEND_MESSAGE': {
            // I'm using Id as a placeholder here. You'll want some way
            // to tie your requests with success/failure receipt.
            return { 
                ...state, 
                [action.id]: { loading: true }
            };
        } case 'SEND_MESSAGE_SUCCESS': {
            return { 
                ...state, 
                [action.id]: { loading: false, success: true }
            };
        } case 'SEND_MESSAGE_FAILURE': {
            return { 
                ...state, 
                [action.id]: { loading: false, success: false }
            };
        }

        default: return state;
    }
}
_

このようにして、リクエストのステータスを簡単に見つけることができ、コンポーネントの読み込み/成功/失敗を気にする必要がありません。

ただし、WebSocketを使用しているため、リクエストの成功/失敗を気にする必要はありません。その場合、通信状態は、ソケットが接続されているかどうかだけです。それがあなたによく聞こえるなら、オープン/クローズのアクションに応答するconnectionレデューサーを書いてください。

制御状態

メッセージの送信を開始するための何かも必要です。チャットアプリの例では、これはおそらく入力フィールドのテキストを送信する送信ボタンです。 制御されたコンポーネント を使用するため、コンポーネント全体のデモは行いません。

ここで重要なのは、制御状態が送信されるメッセージbeforeであることです。今回のコードの興味深い部分は、handleSubmitで何をするかです。

_class ChatForm extends Component {
    // ...
    handleSubmit() {
        this.props.sendMessage(this.state.message);
        // also clear the form input
    }
    // ...
}

const mapDispatchToProps = (dispatch) => ({
    // here, the `sendMessage` that we're dispatching comes
    // from our chat actions. We'll get to that next.
    sendMessage: (message) => dispatch(sendMessage(message))
});

export default connect(null, mapDispatchToProps)(ChatForm);
_

したがって、これはwhereのすべての状態のアドレスです。アクションを使用して標準APIのfetchを呼び出したり、データベースからデータを取得したり、他の任意のソースからデータを取得したりできる汎用アプリを作成しました。あなたの場合は、WebSocketsを使用します。ですから、そのロジックはあなたの行動に生きるべきです。

行動

ここでは、すべてのハンドラーonOpenonMessageonErrorなどを作成します。WebSocketユーティリティをすでに入手しているため、これらはまだかなり汎用的です。個別に設定します。

_function onMessage(e) {
    return dispatch => {
        // you may want to use an action creator function
        // instead of creating the object inline here
        dispatch({
            type: 'RECEIVE_MESSAGE',
            message: e.data
        });
    };
}
_

ここでは非同期アクションに thunk を使用しています。この特定の例では、それは必要ないかもしれませんが、おそらくメッセージを送信して成功/失敗を処理し、単一のsendMessageアクション内から複数のアクションをレデューサーにディスパッチする場合があります。サンクはこの場合に最適です。

すべてを一緒に配線する

最後に、すべてが設定されました。あとは、WebSocketを初期化して適切なリスナーを設定するだけです。ウラジミールが提案したパターン(コンストラクターでソケットをセットアップする)が好きですが、アクションを渡せるようにコールバックをパラメーター化します。次に、WebSocketクラスはすべてのリスナーを設定できます。

WebSocketクラス singleton を作成することにより、アクティブなソケットへの参照を管理する必要なく、アクション内からメッセージを送信できます。また、グローバル名前空間を汚染することも避けます。

シングルトンセットアップを使用することで、new WebSocket()を初めて呼び出すたびに、接続が確立されます。したがって、アプリが起動したらすぐに接続を開く必要がある場合は、componentDidMountAppに設定します。遅延接続で問題がなければ、コンポーネントがメッセージを送信しようとするまで待つだけです。アクションによって新しいWebSocketが作成され、接続が確立されます。

19
Luke

WebSocket専用のクラスを作成して、どこでも使用できます。シンプルで簡潔、そして明確なアプローチです。さらに、WebSocketに関連するすべてのものを1か所にカプセル化します。このクラスからシングルトンを作成することもできますが、一般的な考え方は次のとおりです。

class WS {
  static init() {
    this.ws = new WebSocket('ws://localhost:5432/wss1');
  }
  static onMessage(handler) {
    this.ws.addEventListener('message', handler);
  }
  static sendMessage(message) {
    // You can have some transformers here.
    // Object to JSON or something else...
    this.ws.send(message);
  }
}

index.jsまたはapp.jsのどこかでのみinitを実行しました:

WS.init();

そして今、あなたは任意のアプリケーション層、任意のコンポーネント、任意の場所からメッセージを緩く送信することができます:

WS.sendMessage('My message into WebSocket.');

そして、WebSocketからデータを受信します。

WS.onMessage((data) => {
  console.log('GOT', data);
  // or something else or use redux
  dispatch({type: 'MyType', payload: data});
});

だから、あなたはそれをどこででも、どこででも、どこででもreuxで使うことができます!

14
V. Kovpak

それに関する公式のガイドラインはありません。レンダリングされないため、コンポーネントの使用は混乱を招くと思います。Reduxを使用する場合、アプリケーションの任意の場所でwebsocketからのデータを共有したいと思います。

Websocketマネージャにディスパッチ機能を与えることができます。

const store = createStore(reducer);

const ws = new WebSocketManager(store.dispatch, store.getState);

そしてthis.dispatchクラスメソッド内。

// inside WebSocketManager class
constructor(dispatch, getState) {
    this.dispatch = dispatch;
    this.getState = getState;
}

ミドルウェアを使用して副作用を処理することもできます。これが推奨される方法だと思います。あなたが見ることができる2つの素晴らしいライブラリがあります:

redux-saga

redux-observable

2
Freez