Codementor Events

Simple chat with React.js and ASP.NET Core SignalR

Published Dec 09, 2017Last updated Jun 07, 2018

Introduction

In one of the last posts, we saw how we can make a simple chat with ASP.NET Core SignalR and Angular 5. This time, we will use the same code from the backend and swap Angular with React on the frontend.

There is also a post about making a simple SignalR app with pure ASP.NET Core stack, using Razor Pages with vanilla JavaScript on the client - ASP.NET Core SignalR simple chat.

The code on the backend will be the same, and because of that, we will focus on the client side.

react-create-app

We will use create-react-app, which is an officially supported way to create SPA applications with React. It has no configuration, but it does use Webpack and Babel under the hood.

It is easy to use and requires no configuration, to be precise, you can't configure it. It comes with 4 predefined scripts:

  • start - starts the app in development mode
  • test - starts test runner in interactive watch mode
  • build - builds the app for production, outputs to build folder
  • eject - as an output, you get your app files + configuration files (babel, webpack, environment, paths etc.). You can't go back once you eject an app, you lose the ability to use the _no-configuration _create-react-app tool. However, you can customise and configure everything as you prefer.

We will use only the first one during this post.

The code

We will use the same server-side code as we did with our simple chat with Angular 5.

Let's first create a new empty React application:

create-react-app codingblast

Now we can immediately add SignalR npm package:

npm install @aspnet/signalr-client

Let's add our Chat component:

import React, { Component } from 'react';

class Chat extends Component {
  constructor(props) {
    super(props);
    
    this.state = {
      nick: '',
      message: '',
      messages: [],
      hubConnection: null,
    };
  }

  render() {
    return <div>Here goes chat</div>;
  }
}

export default Chat;

We are using the same properties as we did with Angular chat. We only need nick, message, messages and HubConnection.

First is the user's nickname, second is the current message being typed to the input, messages are for all the messages that we receive from the server.

The user's nickname and the connection to the server we only need to set up once, at the start. Hence, we will add the componentDidMount lifecycle method:

componentDidMount = () => {
    const nick = window.prompt('Your name:', 'John');

    const hubConnection = new HubConnection('http://localhost:5000/chat');

    this.setState({ hubConnection, nick });
}

After we set up the nick and the HubConnection object, we should try to establish the connection with the Server:

componentDidMount = () => {
  const nick = window.prompt('Your name:', 'John');

  const hubConnection = new HubConnection('http://localhost:5000/chat');

  this.setState({ hubConnection, nick }, () => {
    this.state.hubConnection
      .start()
      .then(() => console.log('Connection started!'))
      .catch(err => console.log('Error while establishing connection :('));
  });
}

After that is achieved, we can now start listening for the events from the server. We will just extend our code inside of setState's callback. We will add this code snippet:

this.state.hubConnection.on('sendToAll', (nick, receivedMessage) => {
  const text = `${nick}: ${receivedMessage}`;
  const messages = this.state.messages.concat([text]);
  this.setState({ messages });
});

Let's take a look at our Chat component, how it looks at the moment:

import React, { Component } from 'react';
import { HubConnection } from '@aspnet/signalr-client';

class Chat extends Component {
  constructor(props) {
    super(props);
    
    this.state = {
      nick: '',
      message: '',
      messages: [],
      hubConnection: null,
    };
  }

  componentDidMount = () => {
    const nick = window.prompt('Your name:', 'John');

    const hubConnection = new HubConnection('http://localhost:5000/chat');

    this.setState({ hubConnection, nick }, () => {
      this.state.hubConnection
        .start()
        .then(() => console.log('Connection started!'))
        .catch(err => console.log('Error while establishing connection :('));

      this.state.hubConnection.on('sendToAll', (nick, receivedMessage) => {
        const text = `${nick}: ${receivedMessage}`;
        const messages = this.state.messages.concat([text]);
        this.setState({ messages });
      });
    });
  }
  
  render() {
    return <div>Here goes chat</div>;
  }
}

export default Chat;

That seems fine. Now, we need to add the logic for sending messages to the server. Also, we need to show the actual messages in the view and the button for sending messages.

We will keep the method for sending messages simple:

sendMessage = () => {
  this.state.hubConnection
    .invoke('sendToAll', this.state.nick, this.state.message)
    .catch(err => console.error(err));

    this.setState({message: ''});      
};

After we sent the message, we can clear out the input - message property.

Here is the view to finish our Chat component:

render() {
  return (
    <div>
      <br />
      <input
        type="text"
        value={this.state.message}
        onChange={e => this.setState({ message: e.target.value })}
      />

      <button onClick={this.sendMessage}>Send</button>

      <div>
        {this.state.messages.map((message, index) => (
          <span style={{display: 'block'}} key={index}> {message} </span>
        ))}
      </div>
    </div>
  );
}

Code Sample

You can find the code sample on GitHub: ASP.NET Core SignalR Chat

You can find step by step (commits) on this repository: AspNetCoreSignalR_React

Discover and read more posts from Ibrahim Šuta
get started
post commentsBe the first to share your opinion
Lenin kumar
5 years ago

while try run this program i having following error :
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 OPTIONS http://localhost:5000/chat
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
Policy execution successful.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 72.4913ms 204
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 OPTIONS http://localhost:5000/chat
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
Policy execution successful.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 63.9812ms 200 application/json
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/chat?id=d8429900-97a3-
4f97-86d0-515617b221e5
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
Policy execution successful.
info: Microsoft.AspNetCore.Sockets.Internal.Transports.WebSocketsTransport[0]
05/07/2019 12:06:30: Connection Id d8429900-97a3-4f97-86d0-515617b221e5: S
ocket opened.
info: Microsoft.AspNetCore.Sockets.Internal.Transports.WebSocketsTransport[1]
05/07/2019 12:06:30: Connection Id d8429900-97a3-4f97-86d0-515617b221e5: S
ocket closed.
fail: Microsoft.AspNetCore.Sockets.ConnectionManager[2]
05/07/2019 12:06:30: ConnectionId d8429900-97a3-4f97-86d0-515617b221e5: Fa
iled disposing connection.
System.TypeLoadException: Could not load type ‘System.SpanExtensions’ from assem
bly ‘System.Memory, Version=4.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2
ddd51’.
at Microsoft.AspNetCore.SignalR.Internal.Formatters.TextMessageParser.TryPars
eMessage(ReadOnlyBuffer1& buffer, ReadOnlyBuffer1& payload)
at Microsoft.AspNetCore.SignalR.Internal.Protocol.NegotiationProtocol.TryPars
eMessage(ReadOnlyBuffer1 input, NegotiationMessage& negotiationMessage) at Microsoft.AspNetCore.SignalR.HubEndPoint1.ProcessNegotiate(HubConnectionC
ontext connection)
at Microsoft.AspNetCore.SignalR.HubEndPoint`1.OnConnectedAsync(ConnectionCont
ext connection)
at Microsoft.AspNetCore.Sockets.HttpConnectionDispatcher.ExecuteApplication(S
ocketDelegate socketDelegate, ConnectionContext connection)
at Microsoft.AspNetCore.Sockets.DefaultConnectionContext.WaitOnTasks(Task app
licationTask, Task transportTask)
at Microsoft.AspNetCore.Sockets.DefaultConnectionContext.DisposeAsync()
at Microsoft.AspNetCore.Sockets.ConnectionManager.DisposeAndRemoveAsync(Defau
ltConnectionContext connection)
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 160.3727ms 101

Show more replies