我将要开发一个使用WebSockets的React原生应用程序.我有一个用JavaScript编写的WebSocket库,我只是在这个项目中重复使用它,这太棒了.

我的问题是,作为React/React Native的新手,设置和维护通过WebSocket的所有流量的最佳实践是什么?

最初我的 idea 是在主应用程序组件中创建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,以便所有组件的状态都正确?

推荐答案

到目前为止,答案很好.我只是想补充一点,你保存数据的决定应该是基于数据的多少.我经常提到詹姆斯·纳尔逊.

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

  1. 数据
  2. 通信状态
  3. 控制状态

数据

您的WebSocket连接是通用的,从技术上讲可以返回任何内容,但您收到的消息很可能是数据.例如,假设您正在构建一个聊天应用程序.然后,发送和接收的所有消息的日志(log)将成为数据.您应该使用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减速机来响应打开/关闭操作.

控制状态

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

这里的要点是控制状态是它发送的消息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);

所以,这解决了我们整个州的问题.我们已经创建了一个通用应用程序,可以使用操作调用fetch来获取标准API、从数据库获取数据或任何其他来源.在你的例子中,你想使用WebSockets.所以,这种逻辑应该存在于你的行动中.

Actions

在这里,您将创建所有处理程序: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来表示异步操作.对于这个特定的例子,这可能不是必需的,但您可能会遇到这样的情况:您希望发送一条消息,然后处理成功/失败,并在一个操作中向您的还原程序发送多个操作.Thunk很适合这个案子.

Wiring It All Together

最后,我们把一切都安排好了.我们现在要做的就是初始化WebSocket并设置适当的侦听器.我喜欢Vladimir建议的模式——在构造函数中设置套接字——但我会将回调参数化,以便您可以提交操作.然后WebSocket类可以设置所有的侦听器.

通过将WebSocket类设置为a singleton,您可以从操作内部发送消息,而无需管理对活动套接字的引用.还可以避免污染全局名称空间.

通过使用单例设置,每当你第一次拨打new WebSocket(),你的连接就会建立起来.因此,如果你需要在应用程序启动后立即打开连接,我会在App个应用程序中的componentDidMount个应用程序中进行设置.如果延迟连接正常,那么您可以等待组件try 发送消息.该操作将创建一个新的WebSocket并建立连接.

React-native相关问答推荐

ios 中节列表中的scrollToLocation 不起作用.在 Android 上,列表滚动回索引 0,但在 ios 上不会发生同样的情况

如何渲染一个 svg 以在不同的窗口大小下动态地看起来相同?

NPM INSTALL 在 react-native 项目上失败:无法解析 react 和 react-dom 的依赖树

如何在底部标签导航中添加顶部标签

useState 函数似乎阻止了 Animated.timing 事件?

获取 TypeError:this.InnerNativeModule.configureProps 不是 mac 上 expo 中的函数

打开屏幕后自动显示键盘

如何在react-native 中设置视图的自动高度

react-native 中的layout-only view removal优化是什么?

使用 react-native 单击时如何更改图像和文本 colored颜色 ?

如何在 React Native 中删除警告

因 JVM 堆空间耗尽而导致守护进程到期?

require() 必须有一个字符串文字参数 React Native

react-native run-android 在设备上构建旧版本的代码

更新 this.state.dataSource 不会更新 ListView

无法在react-native元素中扩展 Zip

如何在react-native中显示toast消息

使用 React Native 一次启动多个 Animated.timing

使用 React-Native Navigation 传递数据

在 React Native 中将props传递给外部样式表?