Where to put SignalR hub in React/Redux app?

后端 未结 2 1928
走了就别回头了
走了就别回头了 2021-01-02 13:47

I\'m designing a React website using Redux as the state store, which is primarily to display the current population of items to the user, using live updates to update the it

相关标签:
2条回答
  • 2021-01-02 14:25

    Per the Redux FAQ, the right place for websockets and other similar connections is in Redux middleware.

    Here is a list of existing websocket middle-wares. You can look at the source code of a couple of them and very easily get an idea of how to implement your own custom middle-ware:

    A middleware can dispatch actions. Here's an example of what a socket middleware might look like, and dispatching an action that it listens for:

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

    You need to apply this middleware to your redux store

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

    Later, in your app. This is an action creator

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

    Add a button in your component to dispatch an action via websockets

    class MyComponent extends React.Component {
        handleClick = () => {
            this.props.sendSocketMessage("This goes to the server");
        }
    }
    
    export default connect(null, {sendSocketMessage})(MyComponent)
    
    0 讨论(0)
  • 2021-01-02 14:46

    For people that may find this thread in future.

    This is my custom middleware that only establishes the connection, and registers the handlers. Please note that I only would like to receive data, and not interested in sending data.

    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;
    

    And inside my store.js file

    import signalRMiddleware from '../middlewares/signalRMiddleware';
    
    ...
    
    createStore(rootReducer, {}, composeEnhancers(applyMiddleware(signalRMiddleware)));
    

    UPDATE June 2020 This is how we do it now with the new package @microsoft/signalr https://stackoverflow.com/a/62162742/10232269 This is not using the middleware method. We use Redux, but you don't have to use Redux to utilize this method.

    0 讨论(0)
提交回复
热议问题