Modules and webpack

One of the things I wanted to do is allow my React application to “dynamically” load code from another website for an internal application. The idea being other teams can work on their code and the main application will load the latest deployed version if the user navigates to something that uses it – basically we’re talking about writing plugins.

Note: This is not a great idea for any code you cannot 100% control as you’ll obviously have no idea whether it’s malicious, so if you’re thinking of doing something similar make sure you know what code is being downloaded etc.

Where’s my Module gone

To begin with I wrote an “importer” class in a separate application which successfully loaded code – using axios to download it, Typescript to transpile it (if needed) and the standard Module to compile (module._compile) it, then I slotted the code into a Module and away you go. All worked great until it got deployed within the React application and all of a sudden _compile (and other methods) were missing from Module.

The thing is, webpack basically uses it’s own module system.

webpack modules

The post I wrote on getting up and running with webpack was inspired by the module dilemma as I needed to better understand what webpack was doing under the hood.

If we create and build a webpack project we’ll end up with main.js, here’s a snippet from it for the __webpack_require__ function

function __webpack_require__(moduleId) {
   if(installedModules[moduleId]) {
      return installedModules[moduleId].exports;
   }

   var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
   };

   modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
   module.l = true;
   return module.exports;
}

Note: I’ve removed all the comments just to reduce it to the minimal code.

So within the functions web pack generates for our code/exports etc. we’ll see __webpack_require__ used a fair bit, i.e. to load import/require code (import/require is replaced with __webpack_require__) and looking at the function itself we can see that __webpack_require__ uses it’s own module system, i.e. it caches modules in installedModules (which becomes __webpack_require__.c). We can also see that if the module is not cached a new one is created which looks very different from a NodeModule.

Finally, after the module is created a call is made to the new module, which in this case is one of the functions that’s passed to the __webpack_require__ within main.js which basically returns a function to our code which then becomes the “exports” code returned by the __webpack_require__ function.

Up and running with webpack

Don’t read this blog post if you want to learn about webpack, instead go and read the webpack book.

If you’re still reading this post then let’s go ahead and create a minimal project to start learning about webpack.

Creating our project

  • Create a folder for our starter app., mine’s webpack-starter
  • Within the folder run
    yarn init -y
    

    to create our package.json

  • Now install/add webpack and webpack client using
    yarn add webpack webpack-cli
    
  • Create a src folder off of you project’s root – webpack, by default expects a src folder
  • Add a file index.js to the src folder and we’ll include the minimal code, below
    console.log("Hello World");
    
  • To your package.json file add the following
    "scripts": {
       "build": "webpack --mode development"
    }
    

At this point let’s take a look at what we’ve done. We’ve created a minimal package.json and to this added webpack and the webpack-cli. We’ve created a bare bones entry point (index.js) and a build script.

Now if we run the build script using

yarn build

webpack will generate a dist folder off our root folder along with a main.js file, one look at this and you’ll see far more than you probably expected. Webpack’s created a bootstrap along with a lot of module related code. At the bottom of the file is a function that simply executes the eval function which basically evaluates/runs the code that was in the index.js.

Testing our application

I know it’s a rather minimal application, but it’d be nice to see it actually working, so let’s add the relevant piece to the jigsaw

Run the following

yarn add html-webpack-plugin

We’re going to need to tell webpack to use this plugin, so create a new folder off of the root named config within this create a file name webpack.config.js and here’s the contents for this

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: "Webpack starter",
    }),
  ],
};

If you don’t have serve installed then add it using either the command below or add it globally (see React and serve).

yarn add server

Next up we’ll need to rebuild our dist source but before we do that, we placed the webpack.config.js into the config folder, running the build script as it stands will not pickup this file so you can either move it to the root folder or better still update the script to look like this

"scripts": {
   "build": "webpack --mode development --config config/webpack.config.js"
}

Now running yarn build will create, both an index.html file and main.js file within the dist folder. So now we can run the server. Before we do that, let’s add another script task to package.json, so add the following

"start": "serve dist"

Now we can run yarn start which will start a server up (by default it will be http://localhost:5000). Obviously we’re outputting text to the console so you’ll need to switch your browser to dev mode and view the console output.

Webpack output

When you ran yarn build, webpack used the configuration and the HTML plugin within it to generate our index.html file and also supply the title “Webpack starter”. The output from running webpack looked like this (for me at least)

Hash: af14b7dabddfbc78edb6
Version: webpack 4.35.2
Time: 507ms
Built at: 07/02/2019 10:43:49 AM
     Asset       Size  Chunks             Chunk Names
index.html  181 bytes          [emitted]
   main.js    3.8 KiB    main  [emitted]  main
Entrypoint main = main.js
[./src/index.js] 27 bytes {main} [built]
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {0} [built]
    [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {0} [built]
        + 2 hidden modules
Done in 3.17s.

As you can see the Child html-webpack-plugin for “index.html”: text tells us what code was generated by the HtmlWebpackPlugin

Along with the creation of the index.html file HtmlWebpackPlugin setup a script tag for calling the webpack generated main.js file.

Before we move on let’s change our index.js file to include an import. So create the folder components off of the src folder and create the file helloComponent.js (code for this is shown below)

export default (text) => {
    const element = document.createElement("div");
    element.innerHTML = text;
    return element;
}

and change the index.js code to the following

import hello from "./components/helloComponent";

document.body.appendChild(hello("Hello World"));

Note: again the above is fundamentally the same as the webpack book. The main difference is that I’m wanting to make this look similar to how I’ve learned to use/set-up React as it helps give a feel for how one might transition between React and lower level webpack designed projects or vice versa.

Now run yarn build followed by yarn start. If all goes well you’ll see some interesting changes to the main.js file and ofcourse you should now see actual HTML elements in your browser. I’m far more interested in the main.js changes than the fact we have any output.

More on main.js

If you compare the original main.js file with the new one, you’ll notice that it’s fundamentally the same, it has the webpackBootstrap section as well as all the code setting up the __webpack_require__ properties. Ultimately though the original main.js came down to this line

(function(module, exports) {
eval("console.log(\"Hello World\");\n\n//# sourceURL=webpack:///./src/index.js?");
})

and we can clean this up further just turning it into

(function(module, exports) {
eval("console.log(\"Hello World\");");
})

With regards to the second implementation, although the bulk of the file is still made up with the webpackBootstrap code etc. now we have the following code combined into the single main.js file (I’ve removed comment blocks etc. to clean the code up somewhat)

(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = 
((text) => {\r\n    const element = document.createElement(\"div\");\r\n    element.innerHTML = text;\r\n    return 
element;\r\n});\n\n//# sourceURL=webpack:///./src/components/helloComponent.js?");
}),

/*! no exports provided */
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var 
_components_helloComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./components/helloComponent */ \"./src/components/helloComponent.js\");\n\r\n\r\ndocument.body.appendChild(Object(_components_helloComponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"Hello World\"));\n\n//# sourceURL=webpack:///./src/index.js?");
})

The first function is our helloComponent.js file wrapped in a function, notice how we no longer simply eval the JavaScript but instead eval __webpack_require__. The second function is our index.js.

There’s a lot going on, especially with all the commented code and new lines etc. so let’s clean it up, remove the eval function and format it and then we see that this is the index.js code in a function

__webpack_require__.r(__webpack_exports__);
var _components_helloComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./src/components/helloComponent.js\");
document.body.appendChild(Object(_components_helloComponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"Hello World\"));

The function that this code is within is passed a module, __webpack_exports__ and __webpack_require__, so firstly we call the function __webpack_require__.r with the __webpack_exports__ and this defines the property for either Module or __esModule.

Next we declare a variable named after the folder and filename (i.e. components/helloComponent changes to _components_helloComponent) which in turn is suffixed with the __WEBPACK_IMPORTED_MODULE_0__ (the number 0 increments for each addition module included). This variable is assigned the resultant module after passing the string “./src/components/helloComponent.js” to __webpack_require__. The string is simply a module id, hence as it’s based upon the file system will be unique to the project containing it.

The final line is a call to the document.body.appendChild (as per our index.js) but the function hello (exported from helloComponent) has been replaced by the _components_helloComponent__WEBPACK_IMPORTED_MODULE_0__ variable indexed by the default export.

Basically what’s happened is webpack has parsed our code, replacing the imports with functions which wrap the code from the imports. It’s created it’s own module system using __webpack_require__

What on earth are all the __webpack_require__ properties doing?

What’s happening is that the first function function(modules) (the webpackBootstrap function) is called, next the __webpack_require__ object is setup, with modules and installed modules being assigned to __webpack_require__.m, em>__webpack_require__.c and the rest of the properties setup with functions and so on.

Here’s a list of the current _webpack_require__ properties and their usage (taken from the webpack source)

  • __webpack_require__.s = the module id of the entry point
  • __webpack_require__.c = the module cache
  • __webpack_require__.m = the module functions
  • __webpack_require__.p = the bundle public path
  • __webpack_require__.i = the identity function used for harmony imports
  • __webpack_require__.e = the chunk ensure function
  • __webpack_require__.d = the exported property define getter function
  • __webpack_require__.o = Object.prototype.hasOwnProperty.call
  • __webpack_require__.r = define compatibility on export
  • __webpack_require__.t = create a fake namespace object
  • __webpack_require__.n = compatibility get default export
  • __webpack_require__.h = the webpack hash
  • __webpack_require__.w = an object containing all installed WebAssembly.Instance export objects keyed by module id
  • __webpack_require__.oe = the uncaught error handler for the webpack runtime
  • __webpack_require__.nc = the script nonce

Webpack Loaders

I’m going to end this post with a very quick look at webpack loaders. Whilst there’s nothing wrong with using plain JavaScript, I come from a statically typed world and rather like using TypeScript to catch those stupid mistakes. So let’s set-up webpack to work with TypeScript.

Webpack comes with the concept of loaders, these are basically file processors that become part of the webpack pipeline. For example, they might generate code from configuration files or transpile from other languages etc. Everything then gets bundled via the webpack pipeline.

To add typescript capabilities to webpack we need to edit the webpack.config.js, adding the following

module: {
   rules: [
      {
         test: /\.tsx?$/,
         use: 'ts-loader',
         exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: [ '.tsx', '.ts', '.js' ]
  }

We’ll also need to run

yarn add typescript ts-loader

To installed typescript (if not already installed) and ts-loader, which is used within webpack. Assuming you’re going to now change all .js files (including index.js) to .ts files you’ll also need to amend the webpack.config.js entry to this

entry: './src/index.ts',

Now run your build script as normal and assuming your code is valid TypeScript then you should successfully created a webpack bundle based upon your TypeScript code.

I think that about covers getting webpack up and running, but I’m sure there’ll be more posts on the topic as I delve deeper.

animejs

I was working on some code to display a green circle with a tick (for success) and a red circle with a cross (for failure) within rows in ag-Grid to denote whether service calls succeeded or failed for each row of data. Having the graphics seemed good but what I really wanted was to animate the display of these graphics to make it obvious to the user that these graphics changes/updated.

animejs looked to be a perfect solution to the problem.

There’s lots of great examples using animejs here.

Obviously to begin with I’ve created a React application using the standard scaffolding, and then using yarn installed anime, i.e.

yarn add animejs
yarn add @types/animejs

We’ll create a standard React component (SuccessFailureComponent.tsx) for displaying our new animated component.

import React from 'react';
import anime from "animejs";

interface SuccessFailureProps {
    success: boolean;
}

export class SuccessFailureComponent 
   extends React.Component<SuccessFailureProps, {}> {
      render() {
         // to be implemented
      }
}

In this code we’ve added the property success, which when true will display the green circle with a tick and when false the red circle with the cross.

To begin with we’ll add the following code to draw our graphics to the render function

const backColour = this.props.success ? 
   "#0c3" : 
   "red";
const symbol = this.props.success ? 
   "M9 16l5 5 9-9": 
   "M9 9l14 14 M23 9L9 23";

return (
   <svg className="status" xmlns="http://www.w3.org/2000/svg"
      width="32"
      height="32"
      viewBox="0 0 42 42">
      <circle className="circle"
         cx="16"
         cy="16"
         r="16"
         fill={backColour}/>
      <path d={symbol}
         fill="none"
         stroke="white"
         stroke-width="2.5"   
         stroke-linecap="round"/> :
   </svg>
);

In the above we use the Scalable Vector Graphics element to wrap drawing primitives to first draw a circle with the required fill colour and then using the symbol const which is the drawing of a tick or cross.

If we run the React app at this point (assuming we’ve also added the following component to the App.tsx render method

<SuccessFailureComponent success={false}/>
<SuccessFailureComponent success={true}/>

then we’ll see the two SVG graphics displayed.

Animating our graphics

We’ve come all this way in a post about animation and not animated anything, so let’s now do that. Back in the component, add

componentDidMount() {
   var timeline = anime.timeline({ 
      autoplay: true
   });

   timeline
      .add({
         targets: '.status',
         scale: [{ 
            value: [0, 1], 
            duration: 600, 
            easing: 'easeOutQuad' }
         ]
      });
}

When the application is refreshed you’ll see the graphics scale from nothing to full sized.

In the first line we create the anime timeline which has parameters which will simply start the animation immediately. We then use the add function with the target “status” (our SVG element) to scale the SVG from nothing to full sized with a duration of 600ms. Finally we specify the easing function to use.

We can also add more animation transitions to the timeline, for example the following additions to the timeline code (above) will result in the icons moving right by 10 pixels then returning to where they started

.add({
   targets: '.status',            
   translateX: 10
})
.add({
   targets: '.status',
   translateX: 0
});

Or maybe we’d like to just spin the graphics using

.add({
   targets: '.status',            
   rotate: 360
});

There’s more than just the timeline function, there’s also stagger which allows the animation of multiple elements. Also keyframes which allows us to create an array of keyframes for our animations.

This easings.net is a great resource for seeing how different easing functions might look. If you want to (for example) add a little bounce back to the easing try, easeOutBack.

The anime documentation also has some great examples along with the documentation of fairly simple transitions.

React and CSS

There are several ways to use CSS within React applications.

Inline CSS

We can create a structural type as a variable or const (more likely), for example

const style = {
  background: 'red',
  width: '100px',
  color: 'white'
};

export const MyStyledDiv: React.SFC<{}> = ({
}) => <div style={style}>Hello World</div>

In this case we’re using React.SFC just to reduce the code to a minimal, but fully fledged Components would work in the same way within the render function.

Style based components

With style based components we in essence create a component from the style using the back tick syntax along with the prefix styled.{element type}, so for example

const DivStyle = styled.div`
  background: red;
  width: 100px;
  color: white;
`;

export const MyStyledDiv: React.SFC<{}> = ({
}) => <DivStyle>Hello World</DivStyle>

CSS Modules

We can create separate files for our css (known as CSS Module StyleSheets).

In this case we create a .css file, for example MyStyledDiv.module.css which uses standard CSS syntax, so for example here my file

.myDivStyle {
    background-color: red;
    width: 100px;
    color: white;
}

Now in our component we import the CSS in much the same way as importing components, for example

import styles from './MyStyledDiv.module.css';

export const MyStyledDiv: React.SFC<{}> = ({
}) => <div className={styles.myDivStyle}>Hello World</div>

These style sheets are locally scoped to the component which imports them which helps us to reduce possible name clashes etc.

Standard CSS files

In this case we have standard CSS files, here we have a file named myStyleDiv.css with the following (which is the same as our module.css contents)

.myDivStyle {
    background-color: red;
    width: 100px;
    color: white;
}

Now we again import the CSS like this

import './MyStyledDiv.css';

export const MyStyledDiv: React.SFC<{}> = ({
}) => <div className="myDivStyle">Hello World</div>

Emotion

We can also use third party library’s such as emotion.

Here’s the same example using emotion

const DivStyle = styled.div(props => ({
  background: 'red',
  width: '100px',
  color: 'white'
}))

export const MyStyledDiv: React.SFC<{}> = ({
}) => <DivStyle>Hello World</DivStyle>

Jest (a little bit more)

We’ve covered rudimentary use of Jest as part of React testing, also in a stand alone, now let’s look a little more in depth at common testing requirements.

Unit Test

We write tests using Jest like this

test('Some test description', () => {
});

Note: there are various aliases such as “it”, “fit”, “xit” or “xtest” that can be used in place of “test”.

Obviously the “Some test description” text should be something meaningful, i.e. what is being tested, any specific inputs and what’s the expectation. Ofcourse how much or how little you write as a description is upto the developer. I tend to write what method is being tested, with any special conditions and expected outcome simply because when the tests run I get a good set of information on what’s working and what’s not, however this is down to the developer’s specific tastes.

We can group tests together within a describe function which may allow you to reduce the verbosity of your test descriptions as well as logically group tests, for example

describe('fetch component', () => {
    test("missing URL, expect empty response", () => {
    });

    test("missing invalid, expect error", () => {
    });
});

Note: there are a couple of aliases such as “fdescribe” and “xdescribe” which can be used in place of “describe”.

The output of the Jest test runner would be something like this

fetch component
   √ missing URL
   √ missing invalid

Timeout

The test function also accepts a third argument which can be a timeout (specified in milliseconds). The default timeout is 5 seconds. So for example

test("missing URL, expect empty response", () => {
}, 100);

Paramterized tests

Jest supports paramterized tests using the each function, so for example

test.each`
   input         | output
   ${1}          | ${10} 
   ${5}          | ${50} 
   ${10}         | ${100} 
  `("should return $output when $input is used", ({input, output}) => {
   expect(input * 10).toBe(output);
});

Using a “table”-like layout of data, the first row made up of column names which map to the input(s) and output along with subsequent rows of inputs and outputs to match against delimited using the pipe | operator.

You can also pass in arrays instead of a table structure, i.e.

test.each([[1, 10], [5, 50], [10, 100]])
   (`should return %i when %i is used`, (input, output) => {
   expect(input * 10).toBe(output);
});

We can also supply parameters using the each function of describe meaning we can pass the same parameters through multiple tests. Here’s an example of the above two way of passing parameterized data, but using describe

describe.each`
  input         | output
  ${1}          | ${10} 
  ${5}          | ${50} 
  ${10}         | ${100} 
  `("multiplication with input $input, output $output", ({input, output}) => {

  test("should match", () => {
    expect(input * 10).toBe(output);
  });
})  

describe.each([[1, 10], [5, 50], [10, 100]])
(`multiplication %i, %i`, (input, output) => {

  test("should match", () => {
    expect(input * 10).toBe(output);
  });
});

Only run these tests

During development we might wish to turn off tests whilst we work on a specific set of tests, we can use the only function on either describe or test to basically just run those tests with only specified. For example the following syntax (followed by the normal describe and test parameters)

describe.only 
describe.only.each
test.only
test.only.each

Skipping tests

We can run only certain tests and the opposite (i.e. run all tests except these), skipping tests using the skip

describe.skip
describe.skip.each
test.skip
test.skip.each

Todo

With @types/jest version 24 the todo function was added to test. With this we might have ideas for our tests and want to quickly create stub methods, so we would write

test.todo('multiple number test');

No function should be supplied to a todo test and the Jest tester runner will show the test as a “todo” test within it’s output.

Lifecycle functions

Most unit testing frameworks include the ability to carry out some process before or after a test, for example setting up a test and cleanup, Jest is no different – including beforeAll, beforeEach, afterAll and afterEach functions.

As the names suggest, beforeAll and afterAll are invoked before the tests start and after the tests all complete, respectively. As you’ll have worked out beforeEach and afterEach will be invoked before and after each test is run, respectively.

All life cycle functions accept a function to invoke and an optional timeout.

Expectations

Obviously these previously mentioned tests will all succeed unless we include expectations/assertions. Jest includes the expect function which you’ll often pass the value your system under test returned (or set) along with a function such as toBe which is what you expect the value to be, for using our previous multiplication test

expect(input * 10).toBe(output);

There’s a whole host of functions around expect listed here “Expect”.

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>;

Prettier with ESLint

Prettier is (as it says on their website) am opiniated code formatter.

To begin with I’m going to extend the code from the last post ESLint with airbnb rules. Carry out the following steps

  • yarn add prettier -D
  • yarn add eslint-config-prettier -D

    This line will disable rules that conflict with Prettier

  • yarn add eslint-plugin-prettier -D>
  • Open .eslintc.js and update/add the following value to each key
    plugins: ['prettier']
    
    extends: [
       'prettier',
       'plugin:prettier/recommended'
    ]
    
    rules:  {
       'prettier/prettier': 'error'
    }
    
  • Run yarn lint which we added in the ESLint with airbnb rules post.

The first thing you might notice, as I did, that the ESLint rules required a single quote for strings whereas Prettier requires double quotes, obviously this is not too good if you started running this tool against existing projects, so we want a way to disable such rules in Prettier.

Create a file named .prettierrc.js and place the following code in it

module.exports = {
    singleQuote: true
};

See Prettier configuration for more info.

ESLint with airbnb rules

I’ve covered using lint with TypeScript in my post Using ESLint on TypeScript files.

In this post I’m going to configure up a React application and set-up the project to use Airbnb’s ruleset for ESLint. The Airbnb ruleset appears (but I may be wrong) to be one of the most complete sets and appears to be a bit of a standard for devs.

So let’s create an application and install all the packages we need

  • yarn create react-app your-app-name –typescript
  • yarn add eslint -D
  • yarn add eslint-config-Airbnb -D
  • yarn add eslint-plugin-import -D
  • yarn add eslint-plugin-jsx-a11y -D
  • yarn add eslint-plugin-react -D
  • Add the following to the package.json file, scripts section
    "lint": "eslint --ext .js,.jsx,.ts,.tsx ."
    
  • Add file .eslintignore to the root folder with the following
    build/**/*
    **/*.d.ts
    
  • Add the file .eslintrc.js to the root foldr also, with the following
    module.exports =  {
        parser:  '@typescript-eslint/parser',  // Specifies the ESLint parser
        plugins: ['@typescript-eslint'],
        extends:  [
          'plugin:react/recommended',  // Uses the recommended rules from @eslint-plugin-react
          'plugin:@typescript-eslint/recommended',  // Uses the recommended rules from @typescript-eslint/eslint-plugin
          'airbnb-typescript',
        ],
        parserOptions:  {
            ecmaVersion:  2018,  // Allows for the parsing of modern ECMAScript features
            sourceType:  'module',  // Allows for the use of imports
            ecmaFeatures:  {
            jsx:  true,  // Allows for the parsing of JSX
            },
        },
        rules:  {
        },
        settings:  {
          react:  {
            version:  'detect',  // Tells eslint-plugin-react to automatically detect the version of React to use
          },
        },
      };
    

If all worked, you can no run yarn lint and should see (probably) a fair few errors and warnings. We haven’t even added our own code yet!

Let’s clean a couple of things up first. The serviceWorker.ts file is generated via the React scaffolding, so me might simply want to add the filename to the .eslintignore file, that file looks like this now

build/**/*
**/*.d.ts
serviceWorker.ts

This will greatly reduce the errors/warnings. After re-running lint I have two errors and a warning within the file App.test.tsx, one of which suggest function it (the aliased test function) is not defined.

We have choices here, we can switch the rule to a warning or turn it off, we can add the file to the .eslintignore or we can add a comment block to the top of the file like this

/* eslint-disable */

Now, running lint will ignore this file.

In fact we could using the above with the following to disable a block of code from lint

/* eslint-enable */

We could also disable specific rules across a file/block using

/* eslint no-undef: "off" */

Next up I have index.tsx with an error, ‘document is not defined’. In this case I do not wish to ignore the whole file so we can ignore the specific line of code by placing this comment on the line above the document usage

// eslint-disable-next-line

This is fine but is fairly coarse in that it will ignore any other issues on that line, so we might prefer to disable the next line specific rule, i.e.

// eslint-disable-next-line no-undef

After these changes I ran lint again and had yet more errors and warnings, now in App.tsx. In my case lint states 1 error may be potentially fixable with the –fix option, so let’s see by running yarn lint –fix and then I fix the remaining errors and warnings myself.

At this point you may have had at least one error or warning which you consider to be too strict. The Airbnb rules are quite extensive and therefor quite strict. If you’re wanting to change the rule’s severity or turn the rule off, just go to the .eslintrc.js file and change the code specific to the rule that you want to edit. For example, if you’re coming from C# you might prefix your interfaces with I, by default this is classed as an error – let’s the rule off using

rules:  {
   '@typescript-eslint/interface-name-prefix': 'off'
}

or maybe you would prefer your project moves away from the I prefix but don’t want this to cause an error, then we can change ‘off’ to ‘warn’. So the three states we can set rules to are ‘off’, ‘warn’ and ‘error’.

References

Airbnb JavaScript Style Guide
ESLint Rules

React pure component

I’m using the Airbnb style guide with eslint and one warning mentioned that a very simple Component subclass with no properties or state but it just has a render method, should probably be written as a pure component.

Below is an example of a pure component in it’s most simplistic and minimal form.

import React, { ReactElement } from 'react';

const MyButton = (): ReactElement => (
  <div>My Button</div>
);

export default MyButton;

React also comes with a PureComponent base class, hence we could write

class MyButton extends React.PureComponent {
   render() {
      <div>My Button</div>
   }
}

This is slightly different to our function version of a pure component in that it also handles properties and state, see the documentation for more information.

In both cases, these changes from a standard React Component result in performance increases, so very much worth baring in mind if your components are able to be written in these ways.

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();