Connecting event handlers to React components using TypeScript

In the previous post we created React components which we can pass attributes/properties to. As TypeScript aims to make JavaScript type safe we need to ensure the correct types are expected and passed, so for example here’s some code from the TicTacToe tutorial on the React site, but in this example we’re passing an event handler property from the Board component to the Square component, where it’s wired up to the button

interface SquareProperties {value: number, onclick: (ev: React.MouseEvent<HTMLButtonElement>) => void};

class Square extends Component<SquareProperties, {}> {
  render() {
    return (
      <button className="square" onClick={this.props.onclick}>
        {this.props.value}
      </button>
    );
  }
}

class Board extends Component {
  renderSquare(i: number) {
    return <Square value={i} onclick={() => alert('click')}/>;
  }

JSX, TSX in React, what are they?

React allows us to write JavaScript code in the standard .js files along with TypeScript in the .ts files, but if you’ve created either a TypeScript or JavaScript React app using yarn’s create command, i.e.

yarn create react-app my-app

you’ll notice .jsx or .tsx files. These are similar to Razor, ASP/ASP.NET, JSP and the likes in that they allow us to embed XML (or strictly speaking in our usage XHTML) into source code.

Let’s look at a React component (I’ll use TypeScript in this example but JavaScript jsx components are much the same), in our App.tsx we might want to include our own tag/element, for example

<HelloMessage name="PutridParrot" />

Yes, we’re always obsessed with the Hello World example and this one’s no different.

The above doesn’t really give any context, so here’s the whole of the App.tdx file

import React, { Component } from 'react';
import './App.css';
import HelloMessage from "./components/HelloMessage";

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
            <HelloMessage name="Mark" />
        </header>
      </div>
    );
  }
}

export default App;

Now the HelloMessage element is a React component. In this example I’ve created a components folder off of the src folder and created the file named HelloMessage.tsx, let’s look at the code for this component and then discuss how it works, here’s the code

import React, { Component } from 'react';

export interface HelloMessageProps {name: string;}

class HelloMessage extends Component<HelloMessageProps, {}> {
    render() {
      return (
        <div>
          Hello World {this.props.name}
        </div>
      );
    }
  }

export default HelloMessage;

In this example we’ve imported React, hence need not type React.Component, see Import Declarations for more information on this syntax.

Next up, we’ve created and exported an interface. This acts as the shape of the data that the Component expects to see. As TypeScript supports duck typing, we’re basically saying we expect a thing that looks like HelloMessageProps (i.e. has a name of type string) to be passed into the component. This is our attribute list from the example in App.tsx.

The HelloMessage class extends/inherits from Component and we tell it what data shape we expect to be passed into it, but we do not want to pass any state hence we have a second generic parameter of {}. As we’re writing this as a .tsx we’ve embedded the XHTML along with the use of the property name passed as an attribute in the App.tsx. The render method simply returns the embedded XHTML and that’s it.

We can write the same thing without using the embedded syntax, like this

import React, { Component } from 'react';

export interface HelloMessageProps {name: string;}

class HelloMessage extends Component<HelloMessageProps, {}> {
    render() {
        return React.createElement('div', null, 'Hello World ' + this.props.name);
      }
  }

export default HelloMessage;

Or better still we can use string interpolation by using the ` (back tick), i.e. replacing the Hello World string section with

`Hello World ${this.props.name}`

References

TypeScript specification
React without JSX

Creating a React application by hand

By default it’s probably better and certainly simpler to create a React application using yarn create but it’s always good to know how to create such an application from scratch yourself.

To start with, create a folder for our application and within it create a folder named src, within this create an empty file named index.tsx. Then add the following to this file

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

We don’t actually have React dependencies available to our application at this point so run the following, these will add React as well as TypeScript and TypeScript definitions of React (obviously if you don’t want to work in TypeScript, remove the –typescript switches and the last three yarn commands).

yarn add react --typescript
yarn add react-dom --typescript
yarn add react-scripts
yarn add typescript
yarn add @types/react
yarn add @types/react-dom

yarn will create the package.json file and download the required dependencies. They purpose of the dependencies are probably pretty obvious – the first includes React, the second React-dom and the third gives us the scripts we’re used to when running the code generated React applications, such as supplying the script for yarn start.

Within the package.json add the following, which will add the scripts that we’re used to having available

"scripts": {
   "start": "react-scripts start",
   "build": "react-scripts build",
   "test": "react-scripts test",
   "eject": "react-scripts eject"
}

We’re going to need to also add a folder named public which we’ll place an index.html file in which will act as our default page. Here’s a minimal version copied from a React generated application

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="theme-color" content="#000000" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>Game App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

At this point we should be able to execute

yarn start

and now we should see a React application running in the browser.

If you want to add some CSS to the application, we can simply create index.css in the src folder, create your styles then to use it in index.tsx add the following at the top of the file

import './index.css';

If you’re using VSCode, as I am, you may wish to click on the status bar where it display a version of TypeScript, when index.tsx is open, and set to the most upto date version listed.

Deploying your React application to IIS

We’re going to simply go through the steps for creating a React application (it’ll be the default sample application) through to deployment to IIS.

Create the React application using yarn

First off let’s create a folder in your development folder. Next, via your preferred command prompt application (and ofcourse assuming you have yarn installed etc.). Execute the following from your command prompt within your newly created folder

yarn create react-app my-app --typescript

this will create a bare bones sample application and grab all the dependencies required for it to run.

Does it run?

If you want to verify everything working, just change directory to the application we created (we named it my-app in the previous yarn command) and execute the following command

yarn start

A node server will run and your preferred browser will display the React application (assuming everything worked).

Building our application

At this point our application will happily run via yarn, but for deployment, we need to build it, using

yarn build

This will create a build folder off our our my-app folder.

Creating an IIS website to our React application

We’re going to now simply create a website within IIS which points the the same folder we just created (obviously if you prefer you can deploy the code to the inetpub folder).

In the Internet Information Services (IIS) Manager control panel (this information is specific to IIS 7.0, things may differ in newer versions but the concepts will be the same).

  • Select the Sites connections, right mouse click on it
  • Select Add Web Site
  • Supply a site name, mine’s sample As can be seen the name need not be related to our application name
  • Set the Physical path to build folder created in the previous build step, so for example this will be {path}/my-app/build
  • Port’s 80 and 8080 are usually already set-up and being used by the default web site, so change the port to 5000 and press OK to complete the addition of the website.

At this point if you try to view the new website using http://localhost:5000 you’ll probably get an error, probably stating access is denied. As this example has our source outside of the inetpub folder, we will need to change IIS permissions.

From the Internet Information Services (IIS) Manager control panel

  • Right mouse click on the sample website
  • Select Edit Permissions
  • Select the Security tab
  • Click the Edit button
  • Now click the Add… button
  • If you’re on a domain controller you may need to change Locations… to your machine name, then within the Enter the object names to select, type in IIS_IUSRS and press Check Names, if all went well this will underline the text and no errors will be displayed, now press OK
  • Keep pressing OK on subsequent dialogs until you’re back to the IIS control panel

If you try refreshing the webpage, it’ll probably display Access Denied. We need to allow anonymous access to the site in this case.

From the Internet Information Services (IIS) Manager control panel

  • Select Application Pools
  • Double click on the sample application
  • Change .NET Framework version to No Managed Code

From the Internet Information Services (IIS) Manager control panel

  • Select the sample web site and from the right hand pane of IIS double click on Authentication
  • Right mouse click on Anonymous Authentication and select Application pool identity then press OK

Refreshing the browser should now display the React logo and sample application should be running.

React and serve

During development of our React application, we’ll be using something like

yarn start

When we’re ready to deploy our application, we’ll use

yarn build

Which, in the case of React Typescript, will transpile to JavaScript and package files ready for deployment.

We can also serve the files within the build folder using the serve application.

Installing serve

To install serve, execute

yarn global add serve

This will add server to the global location. Normally (without the global command) packages etc. are installed local to the folder you’re working in. In the case of global packages, these will be installed in C:\Users\{username}\AppData\Roaming\npm\bin on Windows.

To check the location on your installation run

yarn global bin

Running serve on our build

Once serve is installed we can run it using

serve -s build

Note: Obviously if the global location is not in your path you’ll need to prefix the command with the previously found location

Reactive Extensions (Rx) in JavaScript (rxjs)

Reactive Extensions are everywhere – I wanted to try the JavaScript version of the library, so below is a sample React component demonstrating a “fake” service call that might occur within the fetch function. The setTimeout is used simply to simulate some latency in the call.

To add rxjs simple use

yarn add rxjs

Now here’s the sample component code…

import {Observable, Subscriber, Subscription} from 'rxjs';

interface ServiceState {
  data: string;
}

class ServiceComponent extends Component<{}, ServiceState> {

  _subscription!: Subscription;

  constructor(prop: any) {
    super(prop);
    this.state = {
      data : "Busy"
    };
  }

  fetch() : Observable<string> {
    return Observable.create((o: Observer<string>) => {
      setTimeout(() => {
        o.next('Some data');
        o.complete();
      }, 3000);
    });
  }

  componentDidMount() {
    this._subscription = this.fetch().subscribe(s => {
      this.setState({data: s})
    });
  }

  componentWillUnmount() {
    if(this._subscription) {
      this._subscription.unsubscribe();
    }
  }  

  render() {
    return (
      <div>{this.state.data}</div>
    );
  }
}

In this example we have the fetch function returns an instance of an Observable of type string. We then subscribe to this within the componentDidMount function, which is called when the component is inserted into the DOM and then we subscribe to the Observable, updates will be applied to the component’s state.

The componentWillUnmount is called when the component is removed from the DOM and hence we unsubscribe from the Observable.

Adding OneDrive support to an application

I wanted to create an application which stores data for synchronisation between devices, so figured the cheapest way might be to use one of the free storage mediums, in this case OneDrive.

  • Create an application, mine’s a WPF app.
  • Add the NuGet package Microsoft.OneDriveSDK
  • Add the NuGet package Microsoft.OneDriveSdk.Authentication

Note: Windows allows the storage of data to the user’s roaming profile to be stored in the “cloud”. So if your application is solely Windows based this might be a better solution than using OneDrive directly.

Registering our application

Before we get into any coding we’ll need to register our application with the Microsoft Application Registration Portal, instructions can be found here.

Basically you’ll be prompted to log into the Microsoft Application Registration Portal, then you should Add your application, supplying it with a name. Once completed, this will supply us with an application id and application secrets.

As my sample application is a native application then I don’t need to create an app password.

Just click Add Platform and add the platforms you are wanting to support (again mine’s a simple native application).

Writing some code

First off we’ll need to authenticate the user, so we use an authentication adapter. Here’s my code

var msaAuthenticationProvider = new MsaAuthenticationProvider(
   AppId,
   "https://login.live.com/oauth20_desktop.srf",
   new[] { "onedrive.readwrite", "offline_access" });

When you run this code, you’ll be presented with a login screen requiring username/email and password, then a dialog to tell you your application is requesting permissions to access OnDrive and asking you to grant or deny them.

Authorisation scopes, such as onedrive.readonly can be found here.

Now we need to authenticate and access the one drive client.

await msaAuthenticationProvider.AuthenticateUserAsync();

var oneDriveClient = new OneDriveClient(
   "https://api.onedrive.com/v1.0", 
   msaAuthenticationProvider);

Once we get an instance of the OneDriveClient we can interact with the One Drive files/file system. Here’s an example snippet of code which gets to a know folder location, for example imagine we have a folder named Public/MyApp then we could use

var item = await 
   oneDriveClient
      .Drive
      .Root
      .ItemWithPath("Public/MyApp")
      .Request()
      .GetAsync();

We can also use a “special folder” which the application data is stored to by changing the permission scope to onedrive.appfolder, for example

var msaAuthenticationProvider = new MsaAuthenticationProvider(
   AppId,
   "https://login.live.com/oauth20_desktop.srf",
   new[] { "onedrive.appfolder", "offline_access");


await msaAuthenticationProvider.AuthenticateUserAsync();

var item = await oneDriveClient
   .Drive
   .Special
   .AppRoot
   .Request()
   .GetAsync();

Further Reading

OneDrive Dev Center
Official projects and SDKs for Microsoft OneDrive
Using an App Folder to store user content without access to all files

Using Microsoft.Extensions.Logging

The Microsoft.Extensions.Logging namespace includes interfaces and implementations for a common logging interface. It’s a little like common-logging and whilst it’s not restricted to ASP.NET Core it’s got things entry points to work with ASP.NET Core.

What’s common logging all about?

In the past we’ve been hit with the problem of multiple logging frameworks/libraries which have slightly different interfaces. On top of this we might have other libraries which require those specific interfaces.

So for example, popular .NET logging frameworks such as log4net, NLog, Serilog along with the likes of the Microsoft Enterprise Block Logging might be getting used/expected in different parts of our application and libraries. Each ultimately offers similar functionality but we really don’t want multiple logging frameworks if we can help it.

The common-logging was introduced a fair few years back to allow us to write code with a standarised interface, but allow us to use whatever logging framework we wanted behind the scenes. Microsoft’s Microsoft.Extensions.Logging offers a similar abstraction.

Out of the box, the Microsoft.Extensions.Logging comes with some standard logging capabilities, Microsoft.Extensions.Logging.Console, Microsoft.Extensions.Logging.Debug, Microsoft.Extensions.Logging.EventLog, Microsoft.Extensions.Logging.TraceSource and Microsoft.Extensions.Logging.AzureAppServices. As the names suggest, these give us logging to the console, to debug, to the event log, to trace source and to Azure’s diagnostic logging.

Microsoft.Extensions.Logging offers is a relatively simple mechanism for adding further logging “providers” and third part logging frameworks such as NLog, log4net and SeriLog.

How to use Microsoft.Extensions.Logging

Let’s start by simply seeing how we can create a logger using this framework.

Add the Microsoft.Extensions.Logging and Microsoft.Extensions.Logging.Debug NuGet packages to your application.

The first gives us the interfaces and code for the LoggerFactory etc. whilst the second gives us the debug extensions for the logging factory.

Note: The following code has been deprecated and replaced with ILoggerBuilder extensions.

var factory = new LoggerFactory()
   .AddDebug(LogLevel.Debug);

Once the logger factory has been created we can create an ILogger using

ILogger logger = factory.CreateLogger<MyService>();

The MyService may be a type that you want to create logs for, alternatively you can pass the CreateLogger a category name.

Finally, using a standard interface we can log something using the following code

logger.Log(LogLevel.Debug, "Some message to log");

Using other logging frameworks

I’ll just look at a couple of other frameworks, NLog and Serilog.

For NLog add the NuGet package NLog.Extensions.Logging, for Serilog add Serilog.Extensions.Logging and in my case I’ve added Serilog.Sinks.RollingFile to create logs to a rolling file and Serilog.Sinks.Debug for debug output.

Using NLog

Create a file named nlog.config and set it’s properties within Visual Studio as Copy always. Here’s a sample

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" 
   xsi:schemaLocation="NLog NLog.xsd"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   autoReload="true"
   internalLogFile="c:\temp\mylog.log"
   internalLogLevel="Info" >


<targets>
   <target xsi:type="File" name="fileTarget" 
      fileName="c:\temp\mylog.log"      
      layout="${date}|${level:uppercase=true}|${message}
         ${exception}|${logger}|${all-event-properties}" />
   <target xsi:type="Console" name="consoleTarget"
      layout="${date}|${level:uppercase=true}|${message} 
         ${exception}|${logger}|${all-event-properties}" />
</targets>

<rules>
   <logger name="*" minlevel="Trace" writeTo="fileTarget,consoleTarget" />
</rules>
</nlog>

Now in code we can load this configuration file using

NLog.LogManager.LoadConfiguration("nlog.config");

and now the only difference from the previous example of using the LoggerFactory is change the creation of the factory to

var factory = new LoggerFactory()
   .AddNLog();

Everything else remains the same. Now you should be seeing a file named mylog.log in c:\temp along with debug output.

Using serilog

In Serilog’s case we’ll create the logger configuration in code, hence here’s the code to create both a file and debug log

Note: See Serilog documentation for creating the configuration via a config or settings file.

Log.Logger = new LoggerConfiguration()
   .MinimumLevel.Debug()
   .WriteTo.RollingFile("c:\\temp\\log-{Date}.txt")
   .WriteTo.Debug()
   .CreateLogger();

The will create a log file in c:\temp named log-{Date}.txt, where {Date} is replaced with today’s date. Obviously the include of WriteTo.Debug also gives us debug output.

We simply create the logger using familiar looking code

var factory = new LoggerFactory()
   .AddSerilog();

Everything else works the same but now we’ll see Serilog output.

Creating our own LoggerProvider

As you’ve seen in all examples, extension methods are used to AddDebug, AddNLog, AddSerilog. Basically each of these adds an ILoggerProvider to the factory. We can easily add our own provider by implementing the ILoggerProvider interface, here’s a simple example of a DebugLoggerProvider

public class DebugLoggerProvider : ILoggerProvider
{
   private readonly ConcurrentDictionary<string, ILogger> _loggers;

   public DebugLoggerProvider()
   {
      _loggers = new ConcurrentDictionary<string, ILogger>();
   }

   public void Dispose()
   {
   }

   public ILogger CreateLogger(string categoryName)
   {
      return _loggers.GetOrAdd(categoryName, new DebugLogger());
   }
}

The provider needs to keep track of any ILogger’s created based upon the category name. Next we’ll create a DebugLogger which implements the ILogger interface

public class DebugLogger : ILogger
{
   public void Log<TState>(
      LogLevel logLevel, EventId eventId, 
      TState state, Exception exception, 
      Func<TState, Exception, string> formatter)
   {
      if (formatter != null)
      {
         Debug.WriteLine(formatter(state, exception));
      }
   }

   public bool IsEnabled(LogLevel logLevel)
   {
      return true;
   }

   public IDisposable BeginScope<TState>(TState state)
   {
      return null;
   }
}

In this sample logger, we’re going to handle all LogLevel’s and are not supporting BeginScope. So all the work is done in the Log method and even that is pretty simple as we use the supplied formatter to create our message then output it to our log sink, in this case Debug. If no formatter is passed, we’ll currently not output anything, but obviously we could create our own formatter to be used instead.

Finally, sticking with the extension method pattern to add a provider, we’ll create the following

public static class DebugLoggerFactoryExtensions
{
   public static ILoggerFactory AddDebugLogger(
      this ILoggerFactory factory)
   {
      factory.AddProvider(new DebugLoggerProvider());
      return factory;
   }
}

That’s all there is to this very simple example, we create the factory in the standard way, i.e.

var factory = new LoggerFactory()
   .AddDebugLogger();

and everything else works without any changes.

Running tibrvlisten

I keep forgetting this command, so time to blog about it

Occasionally, we’ll want to monitor RV messages being received on our host machine, to do this we can use the tibrvlisten.exe application from the command prompt along with the appropriate arguments. tibrvlisten.exe will filter and display the RV messages as they arrive on the host computer.

An example of use might be

tibrvlisten -service 1234 -network ;123.123.123.123 "mytopics.>"

The -service is the port being used for RV, followed by the IP address of the network it’s on. Note the ; preceding the IP address. Also in this case, I’m filtering what tibrvlisten outputs to the screen by displaying only those messages which start with mytopics. and using the wildcard > symbol.

A few dir/ls/Get-ChildItem Powershell commands

Get-ChildItem is aliased as ls/dir (as well as gci), so is used to list files and directories.

Here’s a few useful commands

Find only folders/directories

ls -Directory

Find only files

ls -File

Find files with a given extension

ls -Recurse | where {$_.Name -like "*.bak"}

Only search to a certain depth

The above will recurse over all folders, but we might want to restrict this by a certain depth level, i.e. in this example up to 2 directories deep

ls -Recurse -Depth 2 | where {$_.Name -like "*.bak"}

Finding the directories with files with a given extension

ls -Recurse -Depth 2 | where {$_.Name -like "*.bak"} | select directory -Unique

The use of -Unique ensure we do not have multiple directories with the same path/name (i.e. for each .bak file found within a single directory)