将SignalR集线器放在React / Redux应用程序中的位置?

时间:2018-05-31 13:15:58

标签: javascript reactjs redux signalr

我正在设计一个React网站,使用Redux作为状态商店,主要是向用户显示当前的项目数量,使用实时更新来使用SignalR更新项目数量。

我想这样做的方法是让SignalR发送项目更新消息,以便在连接到服务器中心时初始化起始填充,以及随着时间的推移通过相同的消息类型进行更新。我将有一个函数接收SignalR消息并将其转换为Redux操作并调度到Redux存储,然后Redux存储将使用该操作来更新状态,然后是UI。

所以这个想法是

  

1)连接到SignalR服务器中心,设置客户端处理程序功能   对于ItemUpdate消息

     

2)当服务器收到来自的Connect()时   客户端,它为该中的所有当前项发送ItemUpdate消息   人口

     

3)客户端从SignalR接收这些消息,   转换为动作并分派到Redux商店

     

4)Redux   根据新项目信息和UI更新商店   显示它

     

5)服务器实现已添加或更新的项目   发送新的ItemUpdate消息以进行更新到客户端

     

6)重复

但是我不确定我应该保留hub单例的位置,因为这似乎与React / Redux设计相反。有人可以建议最好的方法吗?

我的主要应用

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
import 'rxjs';
import store from './store/index';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root') as HTMLElement
);

registerServiceWorker();

我的商店创建文件

import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers/index';
import signalRMiddleware from '../signalr/middleware';

const store = createStore(rootReducer, applyMiddleware(signalRMiddleware));
export default store;

我的服务器出站SignalR消息的中间件(由于我无法访问我需要的集线器对象而被注释掉

export default function signalRMiddleware(store: any) {
    return (next: any) => (action: any) => {
        if (action.signalR) {
            switch (action.type) {
                default:
                    {
                        //const myCurrentState = store.getState().objectWithinState;
                        //_hub.server.methodOnTheServer2(action.type, myCurrentState);
                    }
            }
        }
        return next(action);
    }
}

现在对于传入的消息......这是我从一个在线示例获得的signalR启动函数的shell - 尚未实现,因为我还没有集线器和连接但不确定应该去哪里

export function signalRStart(store: any, callback: Function) {
    _hub = $.connection.myHubName;

    _hub.client.firstClientFunction = (p1: any) => {
        store.dispatch({ type: "SERVER_CALLED_ME", a: p1 });
    }

    _hub.client.secondClientFunction = (p1: string, p2: string) => {
            store.dispatch({ type: "SERVER_CALLED_ME_2", value: p1 + p2 });
        }
    }

    $.connection.hub.start(() => callback());
}

这是我在网站上给出的示例,我发现代码将它们组合在一起,但是我没有看到它如何与我的主索引页面中的React / Redux集成,我必须通过创建的商店到提供者组件,所以我不能把集线器创建放在这个下面,因为你需要信号器中间件组件的集线器,它被传递到商店创建

let _hub;

let store = createStore(
  todoApp,
  // applyMiddleware() tells createStore() how to handle middleware
  applyMiddleware(signalRMiddleware)
)

// Make sure signalr is connected
signalRStart(store, () => {
    render((...),
    document.getElementById("app-container"));
});

有人可以建议将SignalR集成到我的React / Redux应用程序中的最佳方法吗?

2 个答案:

答案 0 :(得分:6)

面向将来可能会发现此话题的人。

这是我的自定义中间件,它仅建立连接并注册处理程序。请注意,我只希望接收数据,而对发送数据不感兴趣。

import {
  JsonHubProtocol,
  HttpTransportType,
  HubConnectionBuilder,
  LogLevel
} from '@aspnet/signalr'; // version 1.0.4

// action for user authentication and receiving the access_token
import { USER_SIGNED_IN } from '../actions/auth';

const onNotifReceived = res => {
  console.log('****** NOTIFICATION ******', res);
};

const startSignalRConnection = connection => connection.start()
  .then(() => console.info('SignalR Connected'))
  .catch(err => console.error('SignalR Connection Error: ', err));

const signalRMiddleware = ({ getState }) => next => async (action) => {
  // register signalR after the user logged in
  if (action.type === USER_SIGNED_IN) {
    const urlRoot = (window.appConfig || {}).URL_ROOT;
    const connectionHub = `${urlRoot}/api/service/hub`;

    const protocol = new JsonHubProtocol();

    // let transport to fall back to to LongPolling if it needs to
    const transport = HttpTransportType.WebSockets | HttpTransportType.LongPolling;

    const options = {
      transport,
      logMessageContent: true,
      logger: LogLevel.Trace,
      accessTokenFactory: () => action.user.access_token
    };

    // create the connection instance
    const connection = new HubConnectionBuilder()
      .withUrl(connectionHub, options)
      .withHubProtocol(protocol)
      .build();

    // event handlers, you can use these to dispatch actions to update your Redux store
    connection.on('OperationProgress', onNotifReceived);
    connection.on('UploadProgress', onNotifReceived);
    connection.on('DownloadProgress', onNotifReceived);

    // re-establish the connection if connection dropped
    connection.onclose(() => setTimeout(startSignalRConnection(connection), 5000));

    startSignalRConnection(connection);
  }

  return next(action);
};

export default signalRMiddleware;

在我的store.js文件中

import signalRMiddleware from '../middlewares/signalRMiddleware';

...

createStore(rootReducer, {}, composeEnhancers(applyMiddleware(signalRMiddleware)));

答案 1 :(得分:0)

根据Redux常见问题解答,the right place for websockets and other similar connections is in Redux middleware

Here is a list of existing websocket middle-wares。您可以查看其中的一些源代码,并且很容易了解如何实现自己的自定义中间件:

中间件可以调度动作。这是一个套接字中间件的外观示例,并调度一个监听的动作:

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

您需要将此中间件应用于您的redux商店

let store = createStore(
    some_reducer,
    applyMiddleware(createMySocketMiddleware)
)

稍后,在您的应用中。这是动作创建者

const sendSocketMessage = message => ({
    type : "SEND_WEBSOCKET_MESSAGE",
    payload : message
}

在组件中添加按钮以通过websocket调度操作

class MyComponent extends React.Component {
    handleClick = () => {
        this.props.sendSocketMessage("This goes to the server");
    }
}

export default connect(null, {sendSocketMessage})(MyComponent)