【问题标题】:How to import SignalR in React Component?如何在 React 组件中导入 SignalR?
【发布时间】:2018-02-21 18:05:35
【问题描述】:

我使用 create-react-app 搭建了初始反应应用程序。

我的仪表板组件:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import $ from 'jquery';
import 'signalr';

class Dashboard extends Component {
   constructor(props) {    
   super(props);
   var connection = $.hubConnection('http://[address]:[port]');
   var proxy = connection.createHubProxy('[hubname]');   

    // atempt connection, and handle errors
    connection.start()
    .done(function(){ console.log('Now connected, connection ID=' + connection.id); })
    .fail(function(){ console.log('Could not connect'); });
}

  render() {
    return (...);
  }
}

export default Dashboard;

现在我从 SignalR 收到以下错误,说没有添加 jQuery,但我已经在上面的行中导入了它:

错误:没有找到 jQuery。请确保之前引用了 jQuery SignalR 客户端 JavaScript 文件。

如果我注释掉 import "signalr"; jQuery 被正确加载,我可以访问模块内的$。为什么会这样?

【问题讨论】:

  • 您能展示一下您是如何在 HTML 中加载脚本的吗?
  • 你可以看到create-react-app
  • 你为什么不这样做:import $ from 'jquery'; 然后import React, { Component } from 'react'; import { Link } from 'react-router-dom';
  • 它没有做任何改变。

标签: javascript jquery reactjs signalr


【解决方案1】:

我发现 Signalr 依赖于jQuery。由于某种原因import $ from 'jquery' 没有设置window.jQuery。这就是为什么需要明确地这样做。

我这样解决了这个问题:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import $ from 'jquery';
window.jQuery = $;
require('signalr');

class Dashboard extends Component {
   // .....   
}

export default Dashboard;

【讨论】:

  • 从 ASP.NET Core 2.1 开始不再是这种情况,SignalR 删除了 jQuery 依赖项。 msdn.microsoft.com/magazine/mt829706
  • @xeiton 你可以使用这个:import * as signalR from '@aspnet/signalr';
  • @MarkSzabo 当我的后端是 Web Api 2 (.NET Framework 4.6.1) 时,这会起作用吗?
  • @mardok 好问题,我不确定。尝试安装 nuget 包,并检查允许的最新版本是什么!
【解决方案2】:

更新:请注意,如果您在 ReactJS 应用程序中使用 Redux,则以下解决方案不一定是最佳解决方案。 It is better to implement signalR as a middleware. You can find the best answer here.

如果你没有使用 Redux,或者你仍然想在 React 组件中实现它,那么请继续阅读: 对于使用最新版 signalR(核心 v2.1)的人,由于 jQuery 不再是 signalR 的依赖项,您可以像这样导入它:

import * as signalR from '@aspnet/signalr';

注意:现在有一个更新版本的信号器 (@microsoft/signalr) 需要不同的设置。此解决方案仅适用于 @aspnet/signalr。 (2020 年 6 月更新

然后像这样使用它:

signalR.HubConnectionBuilder()

这是一个例子:

import React, { PureComponent } from 'react';
import { string } from 'prop-types';
import * as signalR from '@aspnet/signalr';

class SignalR extends PureComponent {
  constructor (props) {
    super(props);

    this.connection = null;
    this.onNotifReceived = this.onNotifReceived.bind(this);
  }

  componentDidMount () {
    const protocol = new signalR.JsonHubProtocol();

    const transport = signalR.HttpTransportType.WebSockets;

    const options = {
      transport,
      logMessageContent: true,
      logger: signalR.LogLevel.Trace,
      accessTokenFactory: () => this.props.accessToken,
    };

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

    this.connection.on('DatabaseOperation', this.onNotifReceived);
    this.connection.on('DownloadSession', this.onNotifReceived);
    this.connection.on('UploadSession', this.onNotifReceived);

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

  componentWillUnmount () {
    this.connection.stop();
  }

  onNotifReceived (res) {
    console.info('Yayyyyy, I just received a notification!!!', res);
  }

  render () {
    return <span />;
  };
};

SignalR.propTypes = {
  connectionHub: string.isRequired,
  accessToken: string.isRequired
};

export default SignalR;

更新:2020年,您可以使用“withAutomaticReconnect()”:

  const connection = new HubConnectionBuilder()
    .withUrl(connectionHub, options)
    .withAutomaticReconnect()
    .withHubProtocol(new JsonHubProtocol())
    .configureLogging(LogLevel.Information)
    .build();

【讨论】:

  • 当我的后端是 Web Api 2 (.NET Framework 4.6.1) 时这会起作用吗?
  • @mardok,我不确定 v2。但我使用的是 v2.1,它就像一个魅力。对于 v2,您可能必须使用 '@aspnet/signalr-client'
  • @xeiton 你怎么知道的?
  • 在我的例子中,我最初使用上面的代码在连接时遇到了一个问题,在连接选项中设置了“skipNegotiation: true”后它起作用了。
  • 说实话,这个“更新”的答案给我带来了很多麻烦。显然,这是一个快速发展的图书馆。我使用了你的 sn-p 的一部分来建立连接,一旦我在托管环境中运行它来解决许多与连接/协议相关的问题,我花了好几个小时。我对 SignalR 的建议:使用 Microsoft 官方文档。
【解决方案3】:

查看 SignalR 没有 jQuery

npm i -D signalr-no-jquery
import { hubConnection } from 'signalr-no-jquery';

const connection = hubConnection('http://[address]:[port]', options);
const hubProxy = connection.createHubProxy('hubNameString');

// set up event listeners i.e. for incoming "message" event
hubProxy.on('message', function(message) {
    console.log(message);
});

// connect
connection.start({ jsonp: true })
  .done(function(){ console.log('Now connected, connection ID=' + connection.id); })
  .fail(function(){ console.log('Could not connect'); });

https://www.npmjs.com/package/signalr-no-jquery

【讨论】:

  • 无法使用这个从客户端进行服务器调用。 publicHubProxy.invoke('getAllGroups'),收到错误为错误:发送失败
【解决方案4】:

这就是我们现在(2020 年)使用新软件包 @microsoft/signalr 的方式。 我们使用 Redux,但您不必使用 Redux 也能使用此方法。

如果您使用的是@microsoft/signalr 包而不是@aspnet/signalr,那么您可以这样设置它。这是我们在 prod 中的工作代码:

import {
  JsonHubProtocol,
  HubConnectionState,
  HubConnectionBuilder,
  LogLevel
} from '@microsoft/signalr';

const isDev = process.env.NODE_ENV === 'development';

const startSignalRConnection = async connection => {
  try {
    await connection.start();
    console.assert(connection.state === HubConnectionState.Connected);
    console.log('SignalR connection established');
  } catch (err) {
    console.assert(connection.state === HubConnectionState.Disconnected);
    console.error('SignalR Connection Error: ', err);
    setTimeout(() => startSignalRConnection(connection), 5000);
  }
};

// Set up a SignalR connection to the specified hub URL, and actionEventMap.
// actionEventMap should be an object mapping event names, to eventHandlers that will
// be dispatched with the message body.
export const setupSignalRConnection = (connectionHub, actionEventMap = {}, getAccessToken) => (dispatch, getState) => {
  const options = {
    logMessageContent: isDev,
    logger: isDev ? LogLevel.Warning : LogLevel.Error,
    accessTokenFactory: () => getAccessToken(getState())
  };
  // create the connection instance
  // withAutomaticReconnect will automatically try to reconnect
  // and generate a new socket connection if needed
  const connection = new HubConnectionBuilder()
    .withUrl(connectionHub, options)
    .withAutomaticReconnect()
    .withHubProtocol(new JsonHubProtocol())
    .configureLogging(LogLevel.Information)
    .build();

  // Note: to keep the connection open the serverTimeout should be
  // larger than the KeepAlive value that is set on the server
  // keepAliveIntervalInMilliseconds default is 15000 and we are using default
  // serverTimeoutInMilliseconds default is 30000 and we are using 60000 set below
  connection.serverTimeoutInMilliseconds = 60000;

  // re-establish the connection if connection dropped
  connection.onclose(error => {
    console.assert(connection.state === HubConnectionState.Disconnected);
    console.log('Connection closed due to error. Try refreshing this page to restart the connection', error);
  });

  connection.onreconnecting(error => {
    console.assert(connection.state === HubConnectionState.Reconnecting);
    console.log('Connection lost due to error. Reconnecting.', error);
  });

  connection.onreconnected(connectionId => {
    console.assert(connection.state === HubConnectionState.Connected);
    console.log('Connection reestablished. Connected with connectionId', connectionId);
  });

  startSignalRConnection(connection);

  connection.on('OnEvent', res => {
    const eventHandler = actionEventMap[res.eventType];
    eventHandler && dispatch(eventHandler(res));
  });

  return connection;
};

然后你会像下面这样调用。请注意,这是一个伪代码。根据您的项目设置,您可能需要以不同的方式调用它。

import { setupSignalRConnection } from 'fileAbove.js';

const connectionHub = '/hub/service/url/events';

export const setupEventsHub = setupSignalRConnection(connectionHub, {
  onMessageEvent: someMethod
}, getAccessToken);

export default () => dispatch => {
  dispatch(setupEventsHub); // dispatch is coming from Redux
};

如果投票有帮助,请告诉我。谢谢

【讨论】:

  • 有没有办法在setupSignalRConnection中处理多个事件处理程序
  • @Akhilesh,是的,这是我通过一个名为“onMessageEvent”的属性传递的对象。您可以向该对象添加任意数量的属性。
  • onMessageEvent 是事件OnEvent 的动作(即一对多)处理程序(即多对多)
  • @Akhilesh,如果我正确理解了您的问题,我之前的回答仍然有效。 “onMessageEvent”只是一个任意事件。您可以将其重命名为任何您想要的名称。所以如果你有多个事件要处理,你需要这样的东西:{ onMessageEvent: someMethod, EventA: eventAHandler, EventB: eventBHandler }。我希望它有所帮助。
  • 我们如何在注销时停止连接?
猜你喜欢
  • 1970-01-01
  • 2020-07-02
  • 1970-01-01
  • 2017-02-12
  • 1970-01-01
  • 1970-01-01
  • 2019-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多