Где и как добавить SignalR в Redux? - PullRequest
0 голосов
/ 03 ноября 2018

Я создаю приложение реагирования, которое реализует SignalR, и до сих пор у меня есть соединение и все прослушиватели в компоненте, где они мне нужны. Проблема в том, что у меня есть создатели действий в Redux, которые просто делают запрос и получают ответ, чтобы позвонить на мой сервер и отправить данные всем остальным клиентам. Как только сервер отправляет событие всем клиентам, один из моих слушателей получает данные и вызывает создателя действия, который просто отправляет действие, чтобы обновить мое избыточное состояние.

Мне кажется, что я не использую создателей действий правильным образом, потому что у меня есть один создатель действий, который просто делает запрос и получает ответ, чтобы вернуть его, а это не меняет состояние.

Если бы в хранилище было соединение с сокетом, я бы просто вызвал одного создателя действия, и логика для генерации или прослушивания события сокета была бы в другом месте.

Это мой компонент,

// --- component.js ---

state = {
    connection: null,
};

async componentDidMount() {
    // handles any network exception and show the error message
    try {
      await this.setupConnection();
    } catch (error) {
      this.showNetworkError(`Whoops, there was an error with your network connection. Please reload the page`);
}

setupConnection = () => {
    let { connection } = this.state;

    this.setState({
      connection: (connection = new HubConnectionBuilder().withUrl(HUB_URL).build()),
    });

    /**
     * LISTENERS that are called from the server via websockets
     */
    connection.on('InsertTodo', data => {
      // action creator
      this.props.add(data);
    });

    connection.on('UpdateTodo', data => {
      // action creator
      this.props.update(data);
    });
}

createTodo = async todo => {

  const { connection} = this.state;

  // action creator
  const createdTodo = await this.props.createTodo(todo);
  if (createdTodo) {
    // the below sentence calls the server to emit/send the todo item to all other clients
    // and the listener in the setupConnection function is executed
    connection.invoke('EmitTodoCreate', createdTodo);
  } else {
    // there was a problem creating the todo
  }
};

Это создатель действий

// --- actionCreators.js ----
// ------------------------
export const add = todo => {
  return async (dispatch) => {
    dispatch({
      type: ADD_TODO,
      payload: todo,
    });
  };
};


export const createTodo = todo => {
  return async (dispatch) => {
    dispatch({
      type: START_REQUEST,
    });

    const response = await postTodo(todo);
    const result = await response.json();

    if (response.ok) {
      dispatch({
        type: SUCCESS_REQUEST,
      });
      // returns the todo item created in order to be sent to the server via websockets
      return result;
    }

    dispatch({
      type: FAILURE_REQUEST,
      error: result.error,
    });
    return null;
  };
};

1 Ответ

0 голосов
/ 12 ноября 2018

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

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

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

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