Published
- 4 min read
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.x and 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.
Initializing the project
First we need to create our project. I will use following scripts to initialize 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-platform.
We need to run start in order to let create-react-app initialize 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 official 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.ts
In 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 initialized 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 accessible 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