redux 使用WebSockets的react原生应用程序中的架构

qgelzfjb  于 2023-11-19  发布在  React
关注(0)|答案(3)|浏览(167)

我有一个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的类,我是否不与WebSocket类中的Redux交互?我是否将所有WebSocket连接处理移动到App组件并将其操作分派到Redux?
这里有什么常见的模式来示例化我的WebSocket类,并确保其中的所有流量都正确地传递到Redux,以便所有组件的状态都正确地传递?

xkftehaa

xkftehaa1#

到目前为止,这里有很好的答案。只是想补充一点,你把数据放在哪里应该取决于数据的类型。James纳尔逊有我经常参考的an excellent article on this topic
对于您的情况,让我们讨论前三种状态类型:
1.数据
1.通信状态
1.控制状态

数据

您的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;
    }
}

字符串
我们不需要(也不应该)在reducer中有任何WebSocket逻辑,因为它们是通用的,不关心数据来自哪里。
另外,请注意,这个reducer能够以完全相同的方式处理发送和接收。这是因为我们的通信状态reducer单独处理网络通信。

通信状态

由于您使用的是WebSockets,因此您想要跟踪的通信状态类型可能与我的示例不同。在使用标准API的应用中,我会跟踪请求何时处于加载失败成功
在我们的聊天应用程序示例中,您可能希望在发送消息时跟踪这些请求状态,但也可能有其他事情需要归类为通信状态。
我们的network reducer可以使用与messages reducer相同的操作:

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 reducer来响应打开/关闭操作。

控件状态

我们还需要一些东西来启动消息的发送。在聊天应用程序的例子中,这可能是一个提交按钮,它发送输入字段中的任何文本。我不会演示整个组件,因为我们将使用一个受控组件。
这里的要点是,控制状态是发送之前的消息。在我们的例子中,有趣的代码是在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。我们已经创建了一个通用应用程序,可以使用操作来调用fetch以获得标准API,从数据库或任何其他来源获取数据。在您的情况下,您希望使用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作为Reducer操作。对于这个特定的例子,这可能不是必要的,但是你可能会遇到这样的情况,你想发送一条消息,然后处理成功/失败,并从一个sendMessage操作中向你的Reducer分派多个操作。Thunk非常适合这种情况。

Wiring It All Together

最后,一切都设置好了。我们现在要做的就是初始化WebSocket并设置相应的侦听器。我喜欢弗拉基米尔建议的模式--在构造函数中设置套接字--但我会参数化你的回调,这样你就可以提交你的操作。然后你的WebSocket类就可以设置所有的侦听器了。
通过将WebSocket类设置为a singleton,您可以从操作内部发送消息,而无需管理对活动套接字的引用。您还可以避免污染全局命名空间。
通过使用单例设置,每当你第一次调用new WebSocket()时,你的连接就会建立。所以,如果你需要在应用程序启动时立即打开连接,我会在AppcomponentDidMount中设置它。如果惰性连接可以,然后你就可以一直等到你的组件尝试发送一条消息。2这个动作将创建一个新的WebSocket,连接将被建立。

f3temu5u

f3temu5u2#

您可以为WebSocket创建专用类并在任何地方使用它。这是一种简单,简洁和清晰的方法。此外,您将在一个地方封装所有与websockets相关的内容!如果您愿意,您甚至可以从这个类中创建singleton,但总体思路是这样的:

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


所以你可以在任何地方使用它,甚至在任何行动或其他地方!

dfddblmv

dfddblmv3#

我认为使用组件是令人困惑的,因为它不会被渲染,我猜如果你使用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;
}


你也可以使用中间件来处理副作用,我认为这是推荐的方式.有两个伟大的库,你可以看看:
redux-saga
redux-observable

相关问题