【问题标题】:Where and how to add SignalR in redux?在 redux 中的何处以及如何添加 SignalR?
【发布时间】:2018-11-03 19:36:24
【问题描述】:

我正在创建一个实现 SignalR 的 react 应用程序,到目前为止,我有我的连接和组件中我需要它们的所有侦听器。问题是我在 Redux 中有动作创建者,它们只是发出请求并获取响应,以便调用我的服务器并将数据发送到所有其他客户端。一旦服务器向所有客户端发出事件,我的一个监听器就会获取数据并调用一个动作创建者,该创建者只是调度一个动作来刷新我的 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;
  };
};

【问题讨论】:

    标签: reactjs redux signalr


    【解决方案1】:

    我认为最好的解决方案是实现一个 Redux 中间件。这很简单,您可以使用身份验证来建立连接,并且您可以根据 SignalR 发出的不同消息来调度动作创建者。

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

    这是我的自定义中间件,用于建立连接并注册处理程序。请注意,我只想接收数据,对发送数据不感兴趣。我使用 REST API 将数据发送到服务器。

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

    【讨论】:

    • 如果有多个connectionHub(多个中心网址)怎么办?
    • @Akhilesh ,我想说与其他集线器网址创建多个“连接”实例。新的“连接”实例将能够处理这些集线器 url 上的新事件。
    猜你喜欢
    • 2019-05-11
    • 2012-02-09
    • 1970-01-01
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-28
    相关资源
    最近更新 更多