Category Archives: TypeScript

Extending our Express based server

In my previous post I showed how to create an Express server in TypeScript. Let’s now extend things…

To start with, create some folders (the names are not important, in that this is not using convention based folder names). We’re going to set things up in a similar way to I’ve done for C# and Java…

  • Add a new folder controllers
  • Add a new folder models

Create a file server.ts in the root folder. Here’s the code from the previous post plus some extras

import express from "express";
import bodyParser from "body-parser";

const server = express();

server.use(bodyParser.json());

server.get("/", (request, response) => {
  response.send("<h1>Hello World</h1>");
});

const port = 4000;
server.listen(port, 
  () => console.log(`Server on port ${port}`)
);

The body-parser exposes middleware etc. in this case we’re adding the ability to work with JSON content type.

Now let’s change the existing route / and add some more, below is the additional code required in server.ts

import * as indexController from "./controllers/indexController";

server.get('/', indexController.index);
server.get('/users', indexController.users);
server.get('/users/create', indexController.create);

// POST implementation
// server.post('/users/create', indexController.create);

We now need to implement the model and the controller, so starting with the model, create the file user.ts within the models folder and it should look like this

export default class User {
  constructor(public firstName: string, public lastName: string) {
  }
}

export const stubData = [
  new User('Scooby', 'Doo'),
  new User('Fred', 'Jones'),
  new User('Velma', 'Dinkley'),
  new User('Daphne', 'Blake'),
  new User('Shaggy', 'Rogers'),
];

The stubData ofcourse is just here to give us some data to start things off.

In the controllers folder, add a new file named indexController.ts and the file should look like this

import { Request, Response } from "express";
import User, { stubData } from '../models/user';

export const index = (req: Request, res: Response) => {
  res.send("<h1>Methods are</h1><ul><li>users</li><li>create</li></ul>")
};

export const users = (req: Request, res: Response) => {
  res.json(stubData);
};

export const create = (req: Request, res: Response) => {
  const newUser = new User(req.query.firstName, req.query.lastName);
  stubData.push(newUser);
  res.json(newUser);
};

// POST implementation
// export const create = (req: Request, res: Response) => {
//   const newUser = new User(req.body.firstName, req.body.lastName);
//   stubData.push(newUser);
//   res.json(newUser);
// };

The scripts for package.json should also be taken from the previous post.

If you now run yarn build then yarn start your server will be up and running. Using the following URL’s

  • http://localhost:4000/
  • http://localhost:4000/users
  • http://localhost:4000/users/create?firstName=Miner&lastName=FortyNiner
  • will result in, the first URL returning a simple help screen using HTML, the second will list the current array of users using JSON and finally the third URL will create a new user, the response will show the new user as JSON, but if you call the users method again you will see the newly added user.

    I’ve also listed the POST implementations which you’d probably more likely use for mutations, but the GET implementations are fine in this instance and easier to test via our preferred browser.

Express server with TypeScript

Express is a popular web application server in JavaScript.

Let’s create a really quick and simple expression server which will be a good starting point for further posts which use this library.

I’ll assume you’ve created a folder, run yarn init and tsc –init, next up run

  • yarn add express
  • yarn add -D @types/express
  • Add a new file, mine’s named server.ts
  • Add the usual scripts
    "scripts": {
      "build": "tsc",
      "start": "node server.js"
    }
    

The code to run up a really simple server is as follows

import express from "express";

const server = express();

server.get("/", (request, response) => {
  response.send("<h1>Hello World</h1>");
});

const port = 4000;
server.listen(port, 
  () => console.log(`Server on port ${port}`)
);

Using our scripts, run yarn build followed by yarn start. The server should start on port 4000, so now navigate to http://localhost:4000 using your preferred browser and that’s it.

We’ve created a server instance on port 4000, we’re routed any root (i.e. /) calls to the response, which in this case returns a simple HTML string.

The “is” type guard in TypeScript

The is keyword can be used to narrow types in TypeScript. What this means is that, for example if we’re testing whether an any parameter is a string a number or whatever, TypeScript will see the type as that type in subsequent code.

For example if we have the following functions

function isString(test: any): test is string {
  return typeof test === "string";
}

function isNumber(test: any): test is number {
  return typeof test === "number";
}

Each function simply takes a parameter of type any then if the return is true the parameter in subsequent code is seen by TypeScript as that type, i.e. Let’s assuming we have a some function which happens to use these functions, like this

function run(a: any) {
  if(isString(a)) {
    console.log(a)
  }
  if(isNumber(a)) {
    console.log(a)
  }
}

If you’re using Visual Code (for example) the TypeScript type popup (display when you move the mouse over the parameter a will show the type to be any in the run parameter list, or in any of the isXXX function parameters. But in the console.log after each isXXX function, TypeScript will interpret the type to be a string (in the case of isString) and a number (in the case if isNumber) hence using intellisense on the variables will list the methods etc. for a string and those for a number respectively

So basically the is keyword will appear to convert the parameter to the specified type if the function returns true.

To look at it another way we could instead write something like this

function isString(test: any): boolean {
    return typeof test === "string";
}

function isNumber(test: any): boolean {
    return typeof test === "number";
}

function run(a: any) {
  if(isString(a)) {
    const b = a as string;
    console.log(b)
  }
  if(isNumber(a)) {
    const b = a as string;
    console.log(b)
  }
}

For completeness, here’s the function syntax for a const style function

const isString = (test: any): test is string => {
  return typeof test === "string";
}

Simple node based HTTP server

In a previous post we used webpack to run an HTTP server, in this post we’re going to create a bare bones HTTP server using the http package.

In your chosen folder run the usual commands

  • yarn init y
  • tsc –init
  • Add a folder named public off of the folder you source will be in (this will be where we add static html files)

Now lets’ add the required packages

  • yarn add node-static @types/node-static

Add the following to the package.json

"scripts": {
  "start": "node server.js",
  "build": "tsc"
}

In the public folder add index.html with the following

<html>
  <head></head>
  <body>
  Hello World
  </body>
</html>

Now let’s add the code to start up our server (mine’s in the file server.ts)

import ns from "node-static";
import http from "http"

const file = new ns.Server("./public");

http.createServer((request, response) => {
    request.addListener("end", () => {
        file.serve(request, response);
    })
    .resume()
})
.listen(4000);

Simple run yarn build then yarn start and the server will start. Navigating your preferred browser to http://localhost:4000/ will then display the HTML file, i.e. Hello World text.

Websockets with JavaScript

Let’s create a JavaScript websocket server.

To start with carry out the following steps…

  • Create a folder, mine’s wsserver and cd to it
  • Run yarn init -y
  • Run tsc –init
  • Run yarn add ws @types/ws
  • Add a file (mine’s server.ts) for our server code
  • Add another file this time for a sample client (mine’s client.ts)
    • Now we will add the scripts, so add the following to package.json

      "scripts": {
        "server": "node server.js",
        "client": "node client.js"
        "build": "tsc"
      }
      

      Let’s add some code, in the server.js put the following

      import WebSocket from "ws";
      
      const wss = new WebSocket.Server({ port: 4000 });
      
      wss.on("connection", ws => {
        ws.on('message', message => {
          console.log('received: %s', message);
        });
      
        ws.on("close", () => {
          console.log("Close connection");
        });
      
        ws.send("Server says Hi");
      });
      

      In the above code we create a new websocket server on port 4000 then we handle any connection’s (once a connection is made the server sends back the message “Server says Hi”. The ws.on “message” will output any messages sent from the client. It should be fairly obvious that the “close” is called when a connection is closed.

      Let’s now put the following in the client.ts

      import WebSocket from "ws";
      
      const ws = new WebSocket("ws://localhost:4000");
      
      ws.onopen = () => {
        ws.send("Client says Hi");
      };
      
      ws.onerror = error => {
        console.log(error);
      }
      
      ws.onmessage = msg => {    
        console.log(`Client onmessage: ${msg.data}`);
      }
      
      ws.onclose = () => {
        console.log("Close");
      };
      

      In the above we open the connection to the web socket server, when the connection is open (onopen) we send a message “Client says Hi” to the server, obviously any errors are sent to the onerror function and any message from the server are routed to the onmessage function, finally onclose is called if the server closes the connection.

      Now run the script command yarn build and then in one terminal run yarn server and in another terminal run yarn client.

      We can also send specific commands to the server, for example in the client add the following to the onopen function

      ws.send("getData");
      

      Within the server add the following

      ws.on("getData", msg => {
        console.log("getData called")
      });
      

      So now when the server receives a getData messages it’s routed to this server function.

      If we have multiple client’s connecting to a server, we can send messages to each client using code, like the following

      wss.clients.forEach(client => {
        if (client.readyState === WebSocket.OPEN) {
          client.send("Broadcasting a  message);
        }
      });
      

      We can also extend the server “connection” function like this

      wss.on("connection", (ws, request) => {
        console.log(request);
      }
      

      The request parameter allows us to check the request.url if we want to change take different actions depending upon the query part of the websocket URL.

      It’s often useful to implement ping/pong capabilities which would allow us to check if the client still exists, here’s rather simplistic example of this type of code.

      wss.on("connection", (ws, request) => {
        ws.isAlive = true;
      
        ws.on("pong", () => {
          console.log("Pong called");
          ws.isAlive = true;
        });
      
        setInterval(function ping() {
          wss.clients.forEach(function each(ws: any) {
            if (ws.isAlive === false)  {
              console.log("Terminated client")
              return ws.terminate();
            }
              
            ws.isAlive = false;
            console.log("Ping client")
            ws.ping();
          });
        }, 10000);
      });
      

Be aware that an async (in TypeScript) when not required adds extra code

Whilst aysnc/await is not formally part of the ES spec (I believe it is available when targetting es2017) we need to be aware of situations where declaring async adds code which is not required.

Specifically I came across some code where a class’s method was marked as async but without an implementation, i.e.

class Derived {
    public async doSomething(): Promise<void> {
    }
}

This compiled/transpiled with the tsconfig.json settings of the project (a React project) and will produce the following code

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
class Derived {
    doSomething() {
        return __awaiter(this, void 0, void 0, function* () {
        });
    }
}

Obviously is not the intention here, i.e. to include the __awaiter code etc. What the code should have looked like is

class Derived {
    public dataBind(): Promise<void> {
        return Promise.resolve();
    }
}

which then produces JavaScript matching exactly this code, i.e.

"use strict";
class Derived {
    dataBind() {
        return Promise.resolve();
    }
}

I know this might seem a little picky, but from what I could see, for every file that includes an empty async method we get an __awaiter created, considering we use minifiers etc. to try to reduce our code size, this obviously makes a difference when code size is important.

JavaScript tagged templates

If you’ve seen code such as the one below (taken from https://www.styled-components.com/docs/basics)

styled.section`
  padding: 4em;
  background: papayawhip;
`

You might be interested in the styled.section code. This is a function and uses template literals as input via template literal syntax. In this usage its known as a tagged template or tagged template literals.

Let’s create our own function to show how this works.

function tt(literals: any, …substitutions: any) {
console.log(literals);
console.log(substitutions);
}

Note: I’m using TypeScript, hence the use of the any keyword, but just remove this for JavaScript code.

If we now run the following code

const name1 = "Scooby";
const name2 = "Doo";

tt`Hello ${name1} World ${name2}`

The following will be logged to the console

[ 'Hello ', ' World ', '' ]
[ 'Scooby', 'Doo' ]

The first values are an array of the literals passed to our function, the second are the substitutions.

Literals will always be an array of substitutions.length + 1 in length. Hence in the example above the literals contains an empty string item at the end to ensure this is the case.

Note: The last item in the literals array is an empty string but ofcourse if we had a string after the ${name2} then this would be the last item, hence to combine these two arrays into a resultant string would require us to ensure we merge all items.

We can therefore combine our two arrays to form a single result using a simple loop, like this


function tt(literals: any, ...substitutions: any) {
  let s = "";

  for (let i = 0; i < substitutions.length; i++) {
    s += literals[i] + substitutions[i];
  }

  return s + literals[literals.length - 1];
}

In the above we’re simply returning a string representing the merge of the two arrays. Remember literals.length will be substitutions.length + 1, hence we simply append that after looping through the smaller of the arrays.

Ofcourse this it not really that useful, if all we wanted to do was return a string we could just create a template literal. Let’s look at a couple of ways of enhancing the functionality.

The first obvious requirement is that we should be able to pass functions into the templates. For example if we have something like this

const t = tt`
 firstName: ${name1};
 lastName: ${name2};
 preferred: ${choice => (choice ? name1 : name2)};
 `;

The choice value needs to be supplied by the calling code and in this example code there’s no easy was to pass this data into t. So first off we need to wrap the tt function within another function and return it, like this

 
function tt(literals: any, ...substitutions: any) {
  return function(options: any) {
    let s = "";

    for (let i = 0; i < substitutions.length; i++) {
      s += literals[i];
      s += typeof substitutions[i] === "function"
        ? substitutions[i](options)
        : substitutions[i];
    }
    return s + literals[literals.length - 1];
  };
}

In the above we’ve also added changes to the original tt function to detect functions within the substitutions. If a function is found whilst looping then it’s invoked by passing in the supplied options.

This implementation then returns a function which, when invoked by passing in some value (in this case named options), will loop through the literals and substitutions and invoking any functions by forwarding the supplied options.

Hence we can call the new tt method like this, for example

t({choice: true});

This would return a string and would return

firstName: Scooby;
lastName: Doo;
preferred: Scooby;

So now for the next enhancement, let’s instead of returning a string, return an object – all we need to do is split on semi-colons to get key/value items where the key will become the object’s property and the value obviously the value stored within the property.

We’ll make a slight change to the code above to this

function tt(literals: any, ...substitutions: any) {
  return function(options: any) {
    let s = "";

    for (let i = 0; i < substitutions.length; i++) {
      s += literals[i];
      s += typeof substitutions[i] === "function"
        ? substitutions[i](options)
        : substitutions[i];
    }

    return toObject(s + literals[literals.length - 1]);
  };
}

The toObject function has been introduced and it’s purpose is to…

  • Take a string which is semi-colon deliminated for each key/value pair
  • Extract each key/value pair which should be deliminated with colons
  • For each entry we will create a property with the name taken from left of the colon on an object and the value right of the colon will be assigned to the property as a value

Here’s the code for toObject

const toObject = (value: any): any =>
  value
    .split(";")
    .map(entry => {
        const e = entry.split(":");
        if(e.length == 2) {
            const key = e[0].trim();
            const value = e[1].trim();
            return [key, value];
        }
        return undefined;
    })
    .filter(entry => entry != undefined)
    .reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1]}), {});

This is not a complete solution as we’re not ensuring validity of the key as a property name. For example you’ll have noticed in styled.component or even React’s styles, that hyphen keys, i.e. background-color or similar would be converted or expected to be backgroundColor. So a simply change would be to convert line 7 to this following

const key = ensureValid(e[0].trim());

and now we introduce a new function to handle all our checks, for now we’ll just ensure the hyphen’s or dot’s are removed and replaced by camelCase

const ensureValid = (key: string): string => 
 key.replace(/[-.]+/g, c => c.length > 0 ? c.substr(1).toUpperCase() : '');

Obviously this function is quite limited, but you get the idea. It can then be used in the toObject function, i.e.

// change
const key = e[0].trim();
// to
const key = ensureValid(e[0].trim());

Taking things a little further

The code below is based upon what was discussed in this post, but extended a little, to start with here’s a more complete implementation of the above code

const ensureValid = (key: string): string => 
    key.replace( /[-.]+([a-z]|[0-9])|[-.]$/ig, (_match, character, pos) => {
        if(pos == 0) {
            return character.toLowerCase();
        }
        else if(character == null) {
            return '';
        }

        return character.toUpperCase();
    });

const toObject = (value: any): any =>
  value
    .split(";")
    .map(entry => {
        const e = entry.split(":");
        if(e.length == 2) {
            const key = ensureValid(e[0].trim());
            const value = e[1].trim();
            return [key, value];
        }
        return undefined;
    })
    .filter(entry => entry != undefined)
    .reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1]}), {});

function tt3(literals: any, ...substitutions: any) {
    return function(options: any) {
        let s = "";

        for (let i = 0; i < substitutions.length; i++) {
            s += literals[i];
            s += typeof substitutions[i] === "function"
                ? substitutions[i](options)
                : substitutions[i];
        }

        return toObject(s + literals[literals.length - 1]);
    };
}

const name1 = "Scooby";
const name2 = "Doo";

const t = tt3`
 -First-6name-: ${name1};
 last-Name: ${name2};
 preferred: ${options => (options.choice ? name1 : name2)};
 `;

console.log(t({choice: true}));

Now let’s have a bit of fun and refactor things to allow us to extract our object from alternate data representations. We’ll create an ini style way to define our objects

const camelCase = (key: string): string => 
    key.replace( /[-.]+([a-z]|[0-9])|[-.]$/ig, (_match, character, pos) => {
        if(pos == 0) {
            return character.toLowerCase();
        }
        else if(character == null) {
            return '';
        }

        return character.toUpperCase();
    });

type splitterFunc = (value: any) => [{key: any; value: any}|undefined];

const standardSplitter = (value: any):  [{key: any; value: any}|undefined] =>
    value
        .split(";")
        .map(entry => {
            const e = entry.split(":");
            if(e.length == 2) {
                const key = camelCase(e[0].trim());
                const value = e[1].trim();
                return [key, value];
            }
            return undefined;
        });

const iniSplitter = (value: any):  [{key: any; value: any}|undefined] =>
        value
            .split("\n")
            .map(entry => {
                const e = entry.split("=");
                if(e.length == 2) {
                    const key = camelCase(e[0].trim());
                    const value = e[1].trim();
                    return [key, value];
                }
                return undefined;
            });
    

const toObject = (value: any, splitter: splitterFunc = standardSplitter): any =>
    splitter(value)
      .filter(entry => entry != undefined)    
      .reduce((obj, entry) => ({ ...obj, [entry![0]]: entry![1]}), {});


function tt3(literals: any, ...substitutions: any) {
    return function(options: any, splitter: splitterFunc = standardSplitter) {
        let s = "";

        for (let i = 0; i < substitutions.length; i++) {
            s += literals[i];
            s += typeof substitutions[i] === "function"
                ? substitutions[i](options)
                : substitutions[i];
        }

        return toObject(s + literals[literals.length - 1], splitter);
    };
}

const name1 = "Scooby";
const name2 = "Doo";
    
const t = tt3`
 -First-6name-: ${name1};
 last-Name: ${name2};
 preferred: ${options => (options.choice ? name1 : name2)};
 `;

 const t1 = tt3`
 -First-6name- = ${name1}
 last-Name = ${name2}
 preferred = ${options => (options.choice ? name1 : name2)}
 `;

console.log(t1({choice: true}, iniSplitter));

Extension methods in TypeScript

Extension methods in C# are a great way to modularise functionality within a class in such a way (for example) to have a basic object with minimal methods and then extend this with further functionality that can be reference only if required.

Hence you might only have a need for the bare bones class in some cases and therefore only need to include that in your code base, alternatively you might also depend upon some advanced functionality that can be added when required. Another use case is adding functionality to third party libraries to extend their functionality.

Ofcourse we might simple extend/inherit from the class to add our new functionality but this ultimately mean’s we’re using a different type. With extension methods in C# we extend a String (for example) with extra functionality as opposed to created a subclass named something else, like ExtendedString.

So can we implement something similar to C#’s extension methods in TypeScript (and therefore JavaScript)? Yes we can, let’s look at a simple example, an Option class for handling defined/undefined values in a functional way. Here’s the Option.ts file

export class Option {
    value: any;

    constructor(value: any) {
        this.value = value;
    }

    isSome(): boolean {
        return this.value != null;  
    } 

    isNone(): boolean {
        return !this.isSome();
    }
}

Hence this is a bare bones implementation which we might extend further with further functionality. Let’s create a file for our extension methods, mine’s named OptionExtensions.ts, which might look something like this

import { Option } from './Option';

declare module './Option' {
    interface Option {
        ifElse(elseValue: any): any;
    }
}

Option.prototype.ifElse = function(elseValue: any): any {
    return this.isSome() ? this.value : elseValue;
}

In the above code we declare a module with the same name as the one which wish to extend and then declare an interface with our new methods. The interface in essences merges with the type to allow us to view our new methods as if they’re on the original Option type.

Finally we can start implementing our new functionality via the JavaScript prototype functionality.

In this example you can see we also have access to the this reference to allow our new functionality to hook into the Option fields etc.

Now let’s look at this in usage

import { Option } from "./Option"
import "./OptionExtensions";

const o = new Option(undefined);
console.log(o.ifElse(999));

That’s it, we pull in the extension methods via the import “./OptionExtensions”; when we want the extended functionality and we can call our extension methods as if they were written into the Option class itself.

TypeScript utility types

Whilst looking into the NonNullable<> type I noticed there’s a bunch of utility types. These types are used to construct other types.

I’m having trouble at this time understanding the use cases for some of these types, so will solely cover those that I can see use cases for.

Starting point

Let’s create a simple interface which we’ll start off with

interface Person {
    name: string;
    age: number;
}

Partial<T>

Use case

We might have a type T with one or more mandatory fields, Partial<T> takes a type T and produces a new type where all fields are optional, so using

type PartialPerson = Partial<Person>;

will create

interface PartialPerson {
    name?: string;
    age?: number;
}

Required<T>

Use case

The opposite to Partial<T> we may have a type with one or more optional fields and we want to produce a type with mandatory fields, so using

interface PartialPerson {
    name?: string;
    age?: number;
}

type RequiredPerson = Required<PartialPerson>

will create

interface RequiredPerson {
    name: string;
    age: number;
}

Readonly<T>

Use case

In some cases we might have a type T and wish to generate a new type where all those fields are marked as readonly, so using

type ReadonlyPerson = Readonly<Person>;

will create

interface ReadonlyPerson {
    readonly name: string;
    readonly age: number;
}

Record<K, T>

Use case

We might wish to generate a new type with fields of type T.

The Record<K, T> takes two types and produces a new type per value passed into the type parameter K of type parameter T, so for example

type RecordPerson = Record<'mother' | 'father', Person>;

will create

interface RecordPerson {
    mother: Person;
    father: Person;
}

Pick<T, K>

Use case

We might have a type T and wish to generate a new type made up of just selected fields.

The Pick<T, K> takes a type T and keys, K, as a union from T. The new type will then be made up of the fields declared in K, for example

type PickPerson = Pick<Person, 'name'>

will create

interface PickPerson {
    name: string;
}

ReturnType<T>

Use case

Useful in situations where we have a function and we want to get the type being returned by the function.

The ReturnType takes a type function T and returns the return type from the function, so let’s create the following code

function getPerson() : Person {
    return { name: "Scooby", age: 12 }
}

type ReturnsPerson = ReturnType<typeof getPerson>;

Getting started with Redux in React

Lot’s of Getting started posts at the moment, and probably a lot more to come. This one’s on using Redux with React and TypeScript.

Let’s create a sample application…

  • yarn create react-app {insert app name} –typescript
  • yarn add redux
  • yarn add react-redux
  • yarn add @types/react-redux
  • We can now delete the App.* files and the *.svg
  • Add folder components to src and also reducers
  • In src/components add Counter.tsx (we’re going to recreate the redux example component for in a tsx). Here’s the code
    import React, { Component } from 'react';
    
    interface CounterProps {
        value: number;
        onIncrement: () => void;
        onDecrement: () => void;
    }
    
    export default class Counter extends Component<CounterProps, {}> {
        constructor(props: CounterProps) {
            super(props);
            this.incrementAsync = this.incrementAsync.bind(this);
            this.incrementIfOdd = this.incrementIfOdd.bind(this);
        }
    
        incrementIfOdd() {
            if (this.props.value % 2 !== 0) {
              this.props.onIncrement()
            }
          }
        
        incrementAsync() {
          setTimeout(this.props.onIncrement, 1000)
        }
    
        render() {
            const { value, onIncrement, onDecrement } = this.props
            return (
              <p>
                Clicked: {value} times
                {' '}
                <button onClick={onIncrement}>
                  +
                </button>
                {' '}
                <button onClick={onDecrement}>
                  -
                </button>
                {' '}
                <button onClick={this.incrementIfOdd}>
                  Increment if odd
                </button>
                {' '}
                <button onClick={this.incrementAsync}>
                  Increment async
                </button>
              </p>
            );
        }
    }
    
  • Now in src/reducers add the file counterReducer.ts and put the following code into it
    export default (state: number = 0, action: any) => {
        switch (action.type) {
          case 'INCREMENT':
            return state + 1
          case 'DECREMENT':
            return state - 1
          default:
            return state
        }
    }
    
  • Finally, replace the contents of the index.tsx with the following
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import * as serviceWorker from './serviceWorker';
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import counter from './reducers';
    import Counter from './components/Counter';
    
    const store = createStore(counter);
    
    const render = () => {
        ReactDOM.render(
            <Provider store={store}>
                <Counter
                    value={store.getState() as number}
                    onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
                    onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
                />,
            </Provider>, 
            document.getElementById('root'));
    }
    
    render();
    store.subscribe(render);
    
    serviceWorker.unregister();
    
  • If everything is correct you should be able to execute yarn start and see the web page with +, – buttons etc. and clicking + or – will increment and decrement the displayed counter.

That’s a lot to take in, so what have we actually done?

Redux supplied a storage container for our application state. In the index.tsx file we create a store passing our reducer (src/reducers/counterReducer.ts) Reducers are called in response to the store.dispatch calls. In this case within index.tsx we dispatch the actions with the type set to strings INCREMENT and DECREMENT. The reducer receives these messages then makes state changes (although not actually changing the state directly but returning a new state).

As the Redux documentation on reducers states, “Remember that actions only describe what happened, but don’t describe how the application’s state changes”.

The next piece of code to look at is

store.subscribe(render);

We’ve wrapped the render code for the application in the render function. Subscribe then allows us to listen to messages on the redux store and calls the render function to render changes. The following part of the Counter code within the index.tsx file then simply assigns the current store state to the Counter value property

value={store.getState() as number

There’s not lot to really say about the Counter component which is standard React code for displaying buttons etc. and exposing properties as used in index.tsx.

More…

This is a simplistic example. Redux documentation states that it’s not advisable to write the store.subscribe code but instead use the connect function provided by React Redux for this functionality.

In this instance we create a src/containers folder and add the file CounterContainer.ts, here’s the code

import {  connect } from 'react-redux'
import Counter from './../components/Counter';

const mapStateToProps = (state: any) => {
    return {
        value: state as number
    }
}

const mapDispatchToProps = (dispatch: any) => {
    return {
        onIncrement: () => dispatch({ type: 'INCREMENT' }),
        onDecrement: () => dispatch({ type: 'DECREMENT' }),
    }
}

export const CounterLink = connect(
    mapStateToProps,
    mapDispatchToProps
)(Counter);

export default CounterLink;

Here we write code to map state to properties and dispatch to properties (if we don’t handle either of these we simply supply a null in place of the function in the connect function.

As you can probably see, in mapStateToProps, the state is supplied and we simply apply it to the value property (which is on our Counter). In mapDispatchToProps we link the onIncrement and onDecrement to the dispatch functions.

Finally in index.tsx replace the Counter component with our newly created and exported CounterLink, i.e. here the new index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import counterReducer from './reducers/counterReducer';
import CounterLink from './containers/CounterContainer';

const store = createStore(counterReducer);

ReactDOM.render(
    <Provider store={store}>
        <CounterLink />
    </Provider>, 
    document.getElementById('root')
);

serviceWorker.unregister();