Notice the "-g" parameter at the end. This ensures that it is installed globally on your machine.
Now going back to recreating your React app. It's time to recreate it with TypeScript. In your command prompt, change directory to the root folder of your ASP.NET Core application. Run the following command prompt:
yarn create react-app clientapp --template typescript
Note in the above command line that "clientapp" is all lowercase. It will not allow you to use uppercase letters. As a result of this command line prompt, your React application will be created into "clientapp". More importantly, it will create it using TypeScript.
Installing SignalR on your React App
Using the command line prompt again, change the directory so you are inside the "clientapp" folder. We are going to install the @microsoft/signalr application from NPM. You can do that by running the following command prompt:
yarn install @microsoft/signalr
As a result, this will install the SignalR library into our React app. Now that we have SignalR in our React app, we are going to make some code changes inside App.tsx which are shown below:
// App.tsx
import React, { useState, useEffect } from "react";
import "./App.css";
import * as signalR from "@microsoft/signalr";
const App: React.FC = () => {
// Builds the SignalR connection, mapping it to /chat
const hubConnection = new signalR.HubConnectionBuilder()
.withUrl("/chat")
.configureLogging(signalR.LogLevel.Information)
.build();
// Starts the SignalR connection
hubConnection.start().then(a => {
// Once started, invokes the sendConnectionId in our ChatHub inside our ASP.NET Core application.
if (hubConnection.connectionId) {
hubConnection.invoke("sendConnectionId", hubConnection.connectionId);
}
});
const SignalRTime: React.FC = () => {
// Sets the time from the server
const [time, setTime] = useState<string | null>(null);
useEffect(() => {
hubConnection.on("setTime", message => {
setTime(message);
});
});
return <p>The time is {time}</p>;
};
const SignalRClient: React.FC = () => {
// Sets a client message, sent from the server
const [clientMessage, setClientMessage] = useState<string | null>(null);
useEffect(() => {
hubConnection.on("setClientMessage", message => {
setClientMessage(message);
});
});
return <p>{clientMessage}</p>
};
return <><SignalRTime /><SignalRClient /></>;
};
export default App;
At present, this code will throw an error. The next step is to integrate SignalR into your ASP.NET Core application.
Installing SignalR on your ASP.NET Core application
Load up Visual Studio 2019 and open your ASP.NET Core application. Go to Tools -> NuGet Package Manager -> Package Manager Console. Run the following command inside the Package Manager Console:
Install-Package Microsoft.AspNetCore.SignalR.Client
This will install SignalR in your ASP.NET Core Application from the NuGet Package Library. Then open up your Startup.cs file and add the following line inside your ConfigureServices method:
services.AddSignalR();
Next, create your SignalR hub. This will inherit the Hub class from the SignalR package. Named ChatHub, we will include a SendConnectionId method. This method is invoked when we start our SignalR connection in our React app:
// ChatHub.cs
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalR_Test.Hubs
{
public class ChatHub : Hub
{
public async Task SendConnectionId(string connectionId)
{
await Clients.All.SendAsync("setClientMessage", "A connection with ID '" + connectionId + "' has just connected");
}
}
}
Inside the SendConnectionId method, we are sending a message to all clients connected. We are sending this message through the setClientMessage method that gets listened out for in our React app.
Now, going back to our Startup.cs file, we are going to find our Configure method. We are going to add a new parameter called "hostApplicationLifetime". This parameter is of type IHostApplicationLifetime (or IApplicationLifetime in .NET Core 2). Then we will add the following code inside our Configure method:
hostApplicationLifetime.ApplicationStarted.Register(() =>
{
var serviceProvider = app.ApplicationServices;
var chatHub = (IHubContext<ChatHub>)serviceProvider.GetService(typeof(IHubContext<ChatHub>));
var timer = new System.Timers.Timer(1000);
timer.Enabled = true;
timer.Elapsed += delegate (object sender, System.Timers.ElapsedEventArgs e) {
chatHub.Clients.All.SendAsync("setTime", DateTime.Now.ToString("dddd d MMMM yyyy HH:mm:ss"));
};
timer.Start();
});
What this does is when the application has started, it starts up a timer which refreshes every second. After every second, it will send the time to all clients connected through SignalR. It will use the setTime method and will send the current server time.
Here is the full code for Startup.cs:
// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SignalR_Test.Hubs;
using System;
namespace SignalR_Test
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime hostApplicationLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chat");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
hostApplicationLifetime.ApplicationStarted.Register(() =>
{
var serviceProvider = app.ApplicationServices;
var chatHub = (IHubContext<ChatHub>)serviceProvider.GetService(typeof(IHubContext<ChatHub>));
var timer = new System.Timers.Timer(1000);
timer.Enabled = true;
timer.Elapsed += delegate (object sender, System.Timers.ElapsedEventArgs e) {
chatHub.Clients.All.SendAsync("setTime", DateTime.Now.ToString("dddd d MMMM yyyy HH:mm:ss"));
};
timer.Start();
});
}
}
}
Once started, your application should look like this: