使用WebSockets的本机应用程序中的体系结构

时间:2017-10-28 20:49:30

标签: javascript reactjs react-native redux

我有一个React Native应用程序,我将使用WebSockets进行构建。我有一个用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 Native应用程序中的许多组件适用于状态,但它不是将扩展React.Component的类,我是否不与Redux中的Redux进行交互WebSocket课程?我是否将所有WebSocket连接处理移至App组件并将操作发送到Redux?

此处的常见模式是实例化我的WebSocket类,并确保其中的所有流量都正确传递给Redux,以便所有组件的状态都能正确汇集?

3 个答案:

答案 0 :(得分:12)

到目前为止,这里有很好的答案。只是想添加 where ,您应该根据数据的类型确定数据。詹姆斯·尼尔森有an excellent article on this topic我经常提到。

对于你的情况,我们来谈谈前三种类型的状态:

  1. 数据
  2. 通讯状态
  3. 控制状态
  4. 数据

    您的WebSocket连接是通用的,可以在技术上返回任何内容,但您收到的消息很可能是数据。例如,假设您正在构建聊天应用。然后,已发送和接收的所有消息的日志将是数据。您应该使用messages reducer将此数据存储在redux中:

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

    我们没有(并且我们不应该)在reducers中有任何WebSocket逻辑,因为它们是通用的,并不关心数据的来源。

    另外,请注意,此减速器能够以完全相同的方式处理发送和接收。这是因为我们的通信状态reducer分别处理网络通信。

    通讯状态

    由于您正在使用WebSockets,因此您要跟踪的通信状态类型可能与我的示例不同。在使用标准API的应用中,我会跟踪请求加载失败成功的时间。

    在我们的聊天应用示例中,您可能希望在发送消息时跟踪这些请求状态,但您可能还希望将其他内容归类为通信状态。

    我们的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;
        }
    }
    

    通过这种方式,我们可以轻松找到请求的状态,而且我们不必为组件中的加载/成功/失败而烦恼。

    但是,您可能不关心任何给定请求的成功/失败,因为您正在使用WebSockets。在这种情况下,您的通信状态可能只是您的套接字是否已连接。如果这对您来说听起来更好,那么只需编写一个connection缩减器来响应打开/关闭时的操作。

    控制状态

    我们还需要一些东西来启动发送消息。在聊天应用程序示例中,这可能是一个提交按钮,用于发送输入字段中的任何文本。我不会演示整个组件,因为我们将使用controlled component

    这里要说的是控制状态是之前发送的消息。在我们的案例中,有趣的代码是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);
    

    所以,那个地址是我们所有州的 。我们创建了一个通用应用程序,可以使用操作为标准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操作中向您的Reducer发送多个操作的情况。 Thunk非常适合这种情况。

    一起布线

    最后,我们已经完成了所有设置。我们现在要做的就是初始化WebSocket并设置适当的监听器。我喜欢Vladimir建议的模式 - 在构造函数中设置套接字 - 但我会参数化你的回调,以便你可以交出你的行动。然后你的WebSocket类可以设置所有的监听器。

    通过创建WebSocket类a singleton,您可以从操作中发送消息,而无需管理对活动套接字的引用。您还可以避免污染全局命名空间。

    通过使用单例设置,无论何时第一次调用new WebSocket(),您的连接都将建立。因此,如果您需要在应用启动后立即打开该连接,我会在componentDidMount App中进行设置。如果延迟连接没问题,那么您可以等到组件尝试发送消息。该操作将创建一个新的WebSocket,并建立连接。

答案 1 :(得分:8)

您可以为WebSocket创建专用课程,并在任何地方使用它。它简单,简洁,清晰。此外,您将把所有与websockets相关的东西封装在一个地方!如果你希望你甚至可以在这个课程中创​​建单身,但总体思路是:

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.jsapp.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});
});

所以你可以在任何动作或其他地方使用redux在任何地方使用它!

答案 2 :(得分:1)

没有关于此的官方指南。我认为使用组件会让人感到困惑,因为它不会被渲染,我想如果你使用Redux,你想在应用程序的任何地方共享来自websocket的数据。

您可以将调度功能提供给Websocket管理器。

const store = createStore(reducer);

const ws = new WebSocket(store.dispatch, store.getState);
//  constructor(dispatch, getState) {
//    this.dispatch = dispatch;
//    this.getState = getState;
//  }

在类方法中使用this.dispatch

您也可以使用中间件来处理副作用,我认为这是推荐的方法。您可以看到两个很棒的库:

redux-saga

redux-observable