Getting started with MOBX 5 and TypeScript 3, React 16.6

When looking around for example applications that use Mobx 5.x combined with Mobx-react 5.xand TypeScript 3.x I did not find any useful example. Most of the examples in the awesome mobx list reference Mobx 3.x and are really outdated.

In my example I will use MOBX to display “Hello World!”. It should showcase a couple of core concepts you will be using in an mobx application.

tl:dr: Source Code for this example can be found here.

Inititalizing the project

First we need to create our project. I will use following scripts to intitialize the project.

Create-react-app with TypeScript

npx create-react-app mobx-example
cd mobx-example
npm install --save typescript @types/node @types/react @types/react-dom @types/jest

npx mv 'src/App.js' 'src/App.tsx' 
npx mv 'src/App.test.js' 'src/App.test.tsx' 
npx mv 'src/index.js' 'src/index.tsx'

npm start

Note: I am using the npm package ‘mv’ to rename the files, this ensures that the script will work cross-plattform.

We need to run start in order to let create-react-app initalize all typescript configuration files.

MOBX

npm install --save mobx mobx-react

In the tsconfig.json you need to ensure that experimental Decorators are enabled. Add following line to your tsconfig:

"experimentalDecorators": true

Getting Started with MOBX

In order to use a MOBX Store you need to create a couple of things:

  • A Mobx Store
  • Configure the provider
  • Use the store in a component

The Mobx Store

Store Directory

It is advisable to keep your stores organized in a single directory like ‘src/stores’.

mkdir src/stores

The Example Store

From the offical documentation to create a store it would be enough to create a class as follows:

mobxStore.ts:

import {observable, action, computed} from 'mobx';

class MobxStore {
    @observable name = "World";

    @computed 
    public get greeting():string {
        return `Hello ${this.name}`;
    }

    @action.bound
    public setName(name:string):void {
        this.name = name;
    }
}

export mobxStore = new MobxStore();

Note: Using @computed is just a demonstration how you could create a calculated value from the current state.

When using TypeScript this is not be enough, you need to create an interface and let our Store implement it to ensure type safety in the react component.

export interface IMobxStore {
    name: string;
    greeting: string;
    setName(name:string):void;
}

Additionally we will move the initialization to its own class.

Finally our mobxStore.ts looks like this:

import {observable, action, computed} from 'mobx';

export interface IMobxStore {
    name: string;
    greeting: string;
    setName(name:string):void;
}

export class MobxStore implements IMobxStore {
     @observable name = "World";

    @computed 
    public get greeting():string {
        return `Hello ${this.name}`;
    }

    @action.bound
    public setName(name:string):void {
        this.name = name;
    }
}

Note: The interface could also be moved into a type definition file.

Store initialization

We will now create a file src/stores/index.tsIn this file we will create an object ‘stores’ that will initialize all stores that we are using in our application.

index.ts:

import { MobxStore } from "./mobxStore";

export const stores = {
    mobxStore: new MobxStore()
}

Configuring the Provider

Since we are ensuring that all stores are inititalized in a single object the configuration of the Provider is very simple In the file src/index.tsx you need to import the store object and the Provider from mobx-react:

import {Provider} from 'mobx-react';
import { stores } from './stores';

Then you need to wrap the <Provider />around the <App />. By using the spread operator for stores you ensure that all stores are registered in the provider.

<Provider {...stores}>
    <App />
</Provider>

Using the Store

In order to use a store in a component you need to inject the store into the component and (most of the time) you will also want to observe the store for changes.

The store will be accessable via the properties. Thus we need to define an interface containing an optional variable ‘mobxStore’ of the type IMobxStore. It needs to be optional due to TypeScript not knowing that the Store is provided by the inject method. If it would be mandatory TypeScript would throw an missing props error when using <App />.

This in turn causes TypeScript to complain that const {greeting} = this.props.mobxStore; is not allowed, as this.props.mobxStore could be ‘undefined’. By adding the Non-null assertion operator ‘!’ you can signal the TypeScript compiler to ignore this warning.

App.tsx:

import React, { Component } from 'react';
import './App.css';
import { observer, inject } from 'mobx-react';
import { IMobxStore } from './stores/mobxStore';

interface AppProps {
  mobxStore?: IMobxStore
}

@inject('mobxStore')
@observer
class App extends Component<AppProps> {
  render() {
    const {greeting} = this.props.mobxStore!;
    
    return (
      <div className="App">
        <header className="App-header">
            {greeting}
          <button onClick={this.clickHandler}>Change Greeting</button>
        </header>
        
      </div>
    );
  }

  private clickHandler = () =>{
    const {setName} = this.props.mobxStore!;
    setName("Bob");
  }
}

export default App;

Conclusion

Now if you run the application you should now see a wonderful “Hello World!” greeting and by clicking on the button it changes to “Hello Bob!” using mobx and typescript.

I hope this makes it a little simpler to get started with React, Mobx and TypeScript.

If you are interested in the project files you can get them from GitHub https://github.com/borgfriend/typescript-react-mobx-example

Ubuntu 18.10: NVidia RTX Drivers

NVidias newly released RTX Graphics cards will not be automatically detected by Ubuntu. In order to get the cards running properly you need to install the NVidia drivers with the version 410 or newer.

In order to install the driver you must run following commands:

sudo add-apt-repository ppa:graphics-drivers
sudo apt update
sudo apt install nvidia-driver-410 
sudo reboot

Dev Environment to Learn TypeScript

TypeScript is basically JavaScript with types. When writing your code you must define the types of your variables and optionally the returns of the functions. If you have been working with Java or C++ these concepts will feel very familiar.

However there is a small problem, TypeScript is not JavaScript – thus you need to use a compiler to convert your code into JavaScript.

This guide will take a look at how to set up a simple developer environment in order to play around with TypeScript.

Tools needed:

  • NodeJS 11 (It does not matter if you are using the LTS Version or the current Version)
  • VSCode

New Project

In order to create your first project you have to create a directory and initialize it with npm. You can use the Command Line and enter following:

mkdir learn-typescript
cd learn-typescript
npm init -y
code . 

When VSCode is open you can open a Terminal using Ctrl-`

TypeScript

Initial Setup

npm install typescript

this will install the typescript compiler (tsc) to your project. You then could run commands like tsc index.ts and it will compile it to index.js. Which you could execute with node index.js.

However you cannot directly debug your code and it is annoying to run the compile before execution.

Add TS-Node

TS-Node is a tool that allows you to directly execute TypeScript code. It uses the existing tsconfig.jsonto process the code.

npm install ts-node

now you could run the code directly via a command like:

ts-node index.ts

Configure VSCode Debugging Tools

Ts-Node also allows you to use the VSCode debugging tools.

Press Ctrl-Shift-P and enter “Debug: Open launch.json” (this will create a launch.json) Select “Node.JS”

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch current file w/ ts-node",
      "protocol": "inspector",
      "args": [
        "${relativeFile}"
      ],
      "cwd": "${workspaceRoot}",
      "runtimeArgs": [
        "-r",
        "ts-node/register"
      ],
      "internalConsoleOptions": "openOnSessionStart"
    }
  ]
}

Now when you press F5 you can run and debug your typescript code. This also allows you to set breakpoints in VSCode.

Your first “Hello World”

Now create a new file called index.ts and add following code:

async function greeter(name: string): Promise<void> {
  await console.log(`Hello ${name}`)
}

greeter("World");

This “Hello World” takes advantages of Modern ES2017 Features “async/await” as well as adding types for TypeScript.

When you press F5 in VSCode this should result in an error as Promises are not available in ES5. We can fix this by configuring the compiler.

Configuring the compiler

In order to configure the compiler you need to run the command:

tsc --init

this will create a tsconfig.json file. with this file you can configure the compiler. When creating it with the command it creates the file with the defaults and comments on what each compiler flag does.

One of the settings you should adjust is the “target” attribute. Currently it defaults to “es5” – this is because Internet Explorer 11 only supports ECMAScript 5.

We are targeting with our code NodeJS 10+, so we can change this value to “es2017”. As you can determine from this table

Running Hello World again

Now press F5 again the output should show you “Hello World”.

Conclusion

Now you are set to experiment some more with TypeScript.

One of the things you can experiment with is changing the target version of the compiler and running tsc index.ts and take a look at the resulting JavaScript code. You will see that compiling to es5 or es6 the compiler adds a lot of extra code to enable functioning code, however when compiling to es2017 it (almost) only strips away the types and leaves the code practically identical.

Attachments:

Compiling the code to ES5

To fix the code to compile to ES5 we first need core-js a library that provides polyfills for older versions of JavaScript.

npm install core-js @types/core-js

now we must add the line index.ts:

import 'core-js/es6/promise';

async function greeter(name: string): Promise<void> {
  await console.log(`Hello ${name}`)
}

greeter("World");

and in our tsconfig.json: we must add:

"lib": [
      "es2015",
      "dom"
    ],

Now your code should compile and run on IE11.