Где разместить концентратор SignalR в приложении React / Redux? - PullRequest
0 голосов
/ 31 мая 2018

Я разрабатываю веб-сайт React, используя Redux в качестве хранилища состояний, который в первую очередь предназначен для отображения текущей совокупности элементов пользователю, используя живые обновления для обновления совокупности элементов с помощью SignalR.

СпособЯ хотел сделать так, чтобы SignalR отправлял сообщения об обновлении элементов, чтобы инициализировать начальную популяцию при подключении к концентратору сервера, а также обновления через тот же тип сообщения, что и время.У меня была бы функция, которая принимает сообщение SignalR и преобразует его в действие Redux и отправляет в хранилище Redux, которое затем использует это действие для обновления состояния, а затем пользовательский интерфейс.

Так что идея

1) Соединение с концентратором сервера SignalR, с функцией обработчика клиента, настроенной для сообщений ItemUpdate

2) Когда сервер получает Connect () от клиента, он отправляет сообщения ItemUpdate для всех текущих элементовв популяции

3) Клиент получает эти сообщения от SignalR, преобразуется в действия и отправляет их в хранилище Redux

4) Redux обновляет хранилище на основе информации о новом элементе и отображает пользовательский интерфейсit

5) Сервер обнаруживает, что элемент был добавлен или обновлен, и отправляет клиенту новое сообщение ItemUpdate для обновления

6) Повтор

Однако яЯ не уверен, где именно я должен хранить синглтон-концентратор, так как это противоречит дизайну 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, которую я получил отонлайн-пример - еще не реализован, так как у меня еще нет концентратора и соединения, и я не уверен, куда это должно идти

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, как на моей главной странице Index, я должен передать созданное хранилище компоненту Provider, и поэтому я не могу поместить создание хаба ниже этого, так как хаб нужен длякомпонент промежуточного программного обеспечения signalr, который передается в создание магазина

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 голосов
/ 05 октября 2018

Для людей, которые могут найти эту ветку в будущем.

Это мое специальное промежуточное ПО, которое только устанавливает соединение и регистрирует обработчики.Обратите внимание, что я хотел бы только получать данные и не заинтересован в отправке данных.

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)));
0 голосов
/ 04 октября 2018

Согласно FAQ Redux, правильное место для веб-сокетов и других подобных соединений находится в промежуточном ПО Redux .

Вот список существующих промежуточных продуктов Websocket .Вы можете посмотреть исходный код нескольких из них и очень легко получить представление о том, как реализовать свое собственное промежуточное программное обеспечение:

Промежуточное программное обеспечение может отправлять действия.Вот пример того, как может выглядеть промежуточное программное обеспечение сокета, и отправка действия, которое он прослушивает:

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

Вам необходимо применить это промежуточное программное обеспечение к вашему магазину притока

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

Позжев вашем приложении.Это создатель действия

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

Добавьте кнопку в свой компонент для отправки действия через веб-сокеты

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

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