Aspnet SignalR and React


Have you ever needed to communicate from server to the react client? We normally use an api to call the server from client and we learned to live with that, but some time you need the opposite. This article is about how to use Aspnet SignalR to send notifications to the React app. On the other words server to client communication!

Aspnet SignalR


SignalR is a open-source library for Microsoft ASP.NET that server to send asynchronous notifications to client-side web applications. It has library includes server-side and client-side JavaScript that can be used in a react web application. There is a good getting started at microsoft: Use ASP.NET Core SignalR with TypeScript, it is a very good beginning but if you wat to mix it with react things can easily go out of hand.

Reactjs 

React is a front-end JavaScript library that although is very simple to learn it take time to be well understood. In React you normally don’t have variables but you have constant, so when ever you make you define a variable with var or let it means that you probably are making a mistake. instead of variables you have state and your job is basically to manage the state of the react application.

So since you don’t have the variable if you have something and you want to change the value you can not just assign a new value to it. This makes things a little bit more interesting.

Redux or not

Well I love Redux, so normally for bigger projects I already have redux or easy peasy and I would use that but i here I am making a sample without, if your project is small you can use this sample , if your project is bigger you can get the idea expand it to redux or whatever you like.

Get the backend ready

So I am going to make this very short as we are focusing of the react side, if you feel you can’t follow please visit the microsoft getting started (like at the beginning of the article) and get more familiar with that.

So we are doing it in dotnet 6, with a WebApi template, just make sure you have dotnet 6 installed, open your favorit terminal and then run this command to make new webapi project by the name of SignalrProject (or create one in visual studio):

dotnet new webapi -n SignalrProject
Code language: JavaScript (javascript)

Open your project in visual studio and add a class by the name of SignalrHub.cs and paste the content below

using Microsoft.AspNetCore.SignalR; public class SignalrHub : Hub { public async Task NewMessage(string user, string message) { await Clients.All.SendAsync("messageReceived", user, message); } }
Code language: C# (cs)

Open your Program.cs and we need to make some changes so it looks like this:

var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSignalR(); builder.Services.AddSwaggerGen(); builder.Services.AddCors(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); app.UseCors(builder => builder .AllowAnyHeader() .AllowAnyMethod() .SetIsOriginAllowed(_ => true) .AllowCredentials() ); } app.UseHttpsRedirection(); app.MapControllers(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapHub<SignalrHub>("/hub"); // Restore this }); app.Run();
Code language: C# (cs)

And that is basically it for the backend (I did not removed rest api capabilities as in real project you need rest api too)

If you run your hub should be ready to send and receive message at https://localhost:port/hab (some environments the casing of the letter mather ex /Hub vs /hub)

React side with typescript

I make the example with typescript if you don’t like it just remove the extra!

To do that you need NodeJS installed with npm. then preferably in another folder that back end run this

npx create-react-app frontend --template typescript

We just need to install the client side library to our react, go to the frontend folder you just created and run this to add the SignalR client side library to your React project:

npm i @microsoft/signalr @types/node
Code language: CSS (css)

open your frontend directory in visual studio code (if you want to !) and create a file named signalRConnection.ts under ./src/signalRConnection.ts

import * as signalR from "@microsoft/signalr"; const URL = process.env.HUB_ADDRESS ?? "https://localhost:5001/hub"; //or whatever your backend port is class Connector { private connection: signalR.HubConnection; public events: (onMessageReceived: (username: string, message: string) => void) => void; static instance: Connector; constructor() { this.connection = new signalR.HubConnectionBuilder() .withUrl(URL) .withAutomaticReconnect() .build(); this.connection.start().catch(err => document.write(err)); this.events = (onMessageReceived) => { this.connection.on("messageReceived", (username, message) => { onMessageReceived(username, message); }); }; } public newMessage = (messages: string) => { this.connection.send("newMessage", "foo", messages).then(x => console.log("sent")) } public static getInstance(): Connector { if (!Connector.instance) Connector.instance = new Connector(); return Connector.instance; } }
Code language: JavaScript (javascript)

So Let’s talk about the code above. We need to have a single connection that is why we use Singleton pattern. When you want to write your code make more functions like public newMessage for sending information to the server and also more delegates like onMessageReceived . The functions are straight forward but the delegates might need more elaborations

Receiving message from the server

On line 19 we stated to listen to an incooming message by the name of messageReceived. So whenever server sends this type of message line 19 is going to call its inner function and with arguments username and message . Here we use delegation (that is very common in react) to call a function that is defined in our components.

Usage

Here is how you use this Connector. In your component , if you have more events you can pass them as named functions. Also I used useState to keep the

import React, { useEffect, useState } from 'react'; import './App.css'; import Connector from './signalRConnection' function App() { const { newMessage, events } = Connector(); const [message, setMessage] = useState("initial value"); useEffect(() => { events((_, message) => setMessage(message)); }); return ( <div className="App"> <span>message from signalR: <span style={{ color: "green" }}>{message}</span> </span> <br /> <button onClick={() => newMessage((new Date()).toISOString())}>send date </button> </div> ); } export default App;
Code language: JavaScript (javascript)

Here I just wanted to keep it simple so instead of adding an input and padding the value I just sent the current date to the server and get it back and display it. You can open 2 browsers and and see that this value is updates simultaneously.

Make more events (server calls):

Essentially we are done here, but it if want some idea how to add more events (how to receive other type of messages). First update the signature definition of your delidage (typescript only )

public events: (onMessageReceived: (username: string, message: string) => void, onSomeOtherServerEventReceived: (payload: Payload) => void ) => void; then you add the this.events = (onMessageReceived, onSomeOtherServerEventReceived) => { this.connection.on("messageReceived", (username, message) => { onMessageReceived(username, message); }); this.connection.on("somethingDefinedOnServer", (payload) => { somethingDefinedOnServer(payload); }); };
Code language: JavaScript (javascript)

then use it in your component :

useEffect(() => { const handleMessageReceived = (_, message) => setMessage(message); const handleSomeOtherServerEventReceived = (payload) => { // do something... } events(handleMessageReceived ,handleSomeOtherServerEventReceived ); });
Code language: Java (java)

Leave a Reply

Your email address will not be published.