Redux 架构中的自定义 Websocket

Posted

技术标签:

【中文标题】Redux 架构中的自定义 Websocket【英文标题】:Custom Websocket in Redux Architecture 【发布时间】:2018-09-10 15:16:56 【问题描述】:

我对这个主题的了解越多,就好像掉进了兔子洞。这是一个新的交易应用程序,它通过基于请求-响应范例的网络套接字接收实时数据。存在三个单独的 SPA,其中除了初始加载之外,每个用户操作都会触发对具有新 MDXQuery 的数据存储的调用。所以反过来我需要在 componentDidMount() 以及相应的 ActionCreators 上进行新的订阅。我想简化代码以避免重复代码和冗余。

下面的代码有助于建立一个新的订阅通道,以通过 web-socket 流式传输响应。(与大多数带有指定打开、关闭、发送的 sockets.io 代码不同)

    this.subscription = bus.channel(PATH,  mode: bus.wsModes.PULL ).createListener(this.onResponse.bind(this));
this.subscription.subscribe(MDXQuery);

如果我阅读了 REDUX 文档,我应该将 Web 套接字代码放在哪里?它提到创建自定义中间件。 链接:https://redux.js.org/faq/codestructure#where-should-websockets-and-other-persistent-connections-live

但我不太确定如何使用此自定义 Web 套接字代码来构建我自己的中间件或在组件级别进行操作将有助于模仿此策略。

const createMySocketMiddleware = (url) => 
return storeAPI => 
    let socket = createMyWebsocket(url);

    socket.on("message", (message) => 
        storeAPI.dispatch(
            type : "SOCKET_MESSAGE_RECEIVED",
            payload : message
        );
    );

    return next => action => 
        if(action.type == "SEND_WEBSOCKET_MESSAGE") 
            socket.send(action.payload);
            return;
        

        return next(action);
    

任何设计输入都会有帮助!

【问题讨论】:

【参考方案1】:

我写了那个常见问题解答条目和示例。

如果我理解您的问题,您是在询问如何在运行时动态创建额外订阅?

由于 Redux 中间件可以看到通过中间件管道传递的每个已分派的操作,因此您可以分派仅用作中间件执行某些操作的命令的操作。现在,我不确定 MDXQuery 是什么,也不清楚您想对从这些订阅收到的消息做什么。为了这个示例,我假设您希望在收到订阅消息时调度 Redux 操作,或者可能对它们执行一些自定义逻辑。

您可以编写一个自定义中间件来侦听"CREATE_SUBSCRIPTION""CLOSE_SUBSCRIPTION" 之类的操作,并可能接受一个回调函数以在收到消息时运行。

这可能是这样的:

// Add this to the store during setup
const subscriptionMiddleware = (storeAPI) => 
    let nextSubscriptionId = 0;
    const subscriptions = ;
    const bus = createBusSomehow();

    return (next) => (action) => 
        switch(action.type) 
            case "CREATE_SUBSCRIPTION" : 
                const callback = action;
                const subscriptionId = nextSubscriptionId;
                nextSubscriptionId++;

                const subscription = bus.channel(PATH,  mode: bus.wsModes.PULL )
                                        .createListener((...args) => 
                                            callback(dispatch, getState, ...args);
                                        );
                subscriptions[subscriptionId] = subscription;
                return subscriptionId;
            
            case "CLOSE_SUBSCRIPTION" : 
                const subscriptionId = action;
                const subscription = subscriptions[subscriptionId];

                if(subscription) 
                    subscription.close();
                    delete subscriptions[subscriptionId];
                
                return;
            
        
    


// Use over in your components file
function createSubscription(callback) 
    return type : "CREATE_SUBSCRIPTION", callback ;


function closeSubscription(subscriptionId) 
    return type :  "CLOSE_SUBSCRIPTION", subscriptionId;


// and in your component:
const actionCreators = createSubscription, closeSubscription;

class MyComponent extends React.Component 
    componentDidMount() 
        this.subscriptionId = this.props.createSubscription(this.onMessageReceived);
    

    componentWillUnmount() 
        this.props.closeSubscription(this.subscriptionId);
    


export default connect(null, actionCreators)(MyComponent);

【讨论】:

你好,马克,首先,这对我来说是一个***的时刻,在过去的两年里,我一定读过你的很多博文,这些博文让我在 React-Redux 应用程序中受益匪浅我开发的。 简化的 MDX 查询有点像 sql 查询,但用于大数据分析时要复杂一些。 SELECT NON EMPTY [Measures].[Stock_Value],[Measures].[Stock_Prices],[Measures].[Stock_Hike_Rate] ON COLUMNS,NON EMPTY Hierarchize(DrilldownLevel([Stock].[StockId].[ALL].[ AllMember])) 来自 [StockTable] 单元格属性值的行 是的,这个想法是动态地创建对 Web 套接字的订阅以获取新的股票数据。 1. 当组件加载到屏幕上时或 2. 用户通过从下拉列表中选择一些新参数在页面上执行一些操作。大多数屏幕都有使用 React-vis 图表或表格创建的复杂图形。 // onResponse() 是一个示例 Promise。需要提供一个 Promise 来处理订阅响应。 let subscription = bus.channel('/StockMarket', mode: bus.wsModes.PULL ).createListener(this.onResponse.bind(this)); 哈,粉丝?很好:) 很高兴我的帖子很有用。该示例是否有助于回答您的问题(或者至少让您了解这在您自己的应用中是如何工作的),还是您仍然对某些方面感到困惑?【参考方案2】:

我为我自己的问题尝试了您的解决方案,该解决方案涉及仅在用户登录时创建套接字实例,这是我的代码的外观:

const socketMiddleWare = url => store => 
  const socket = new SockJS(url, [], 
    sessionId: () => custom_token_id
  );
  return next => action => 
    switch (action.type) 
      case types.USER_LOGGED_IN:
        
          socket.onopen = e => 
            console.log("Connection", e.type);

            store.dispatch(
              type: types.TOGGLE_SOCK_OPENING
            );

            if (e.type === "open") 
              store.dispatch(
                type: types.TOGGLE_SOCK_OPENED
              );
              createSession(custom_token_id, store);
              const data = 
                type: "GET_ACTIVE_SESSIONS",
                JWT_TOKEN: Cookies.get("agentClientToken")
              ;
              store.dispatch(
                type: types.GET_ACTIVE_SESSIONS,
                payload: data
              );
            
          ;
          socket.onclose = () => 
            console.log("Connection closed");
            store.dispatch(
              type: types.POLL_ACTIVE_SESSIONS_STOP
            );
            // store.dispatch( type: TOGGLE_SOCK_OPEN, payload: false );
          ;
          socket.onmessage = e => 
            console.log(e)
          ;
          if (
            action.type === types.SEND_SOCKET_MESSAGE
          ) 
            socket.send(JSON.stringify(action.payload));
            return;
           else if (action.type === types.USER_LOGGED_OUT) 
            socket.close();
          
          next(action);
        
      default:
        next(action);
        break;
    
  ;
;

虽然它不起作用,但你能指出我正确的方向吗?谢谢。

【讨论】:

以上是关于Redux 架构中的自定义 Websocket的主要内容,如果未能解决你的问题,请参考以下文章

如何在通过 NPM 分发时管理 React 组件(Redux、CSS)的自定义方面

python中的自定义pivot_ui聚合器?

WebSocket 服务器中的 Redux

具有自定义子组件的自定义 Blazor 组件

为啥 Azure 将不同的 HTML 嵌入到自定义和内置策略的自定义 UI 注册表单中?

为 redux 承诺的 Websocket