React with MobX 6 — with persist and without decorators

We make a starter that combines Reactjs and Mobx 6, we make it with persist (save to local-storage) and without decorators 🚀

Idan Levi
3 min readApr 30, 2021
React + MobX

First of all, we put the result (in a sandbox), you can see the connection between components and the usage in stores and local-storage data.
In the example project, we have 4 component:
1) RootComponent — parent with 3 child components
2) ComponentA
3) ComponentB
4) ComponentC

react with mobx6 (no decorators, multiple stores, persist) starter (Codesandbox)

MobX Installation

After we create a React project we need to install Mobx:

yarn add mobx mobx-react 

Create Stores without decorators

We create UserStore without decorators,
decorators make problems with react (you need to config the syntax and there is a problem to run the projectreact-scripts) and it solves that.
To do this, we need to add makeAutoObservable to the constructor (you can use makeObservable(target, annotations?, options?) also, but in a different way)
functions with get be computed, other function will be actions and the fields will be observables

import { makeAutoObservable } from "mobx";class UserStore {
constructor() {
makeAutoObservable(this)
}
username = ''
color = null
setUser(username, color) {
this.username = username
this.color = color
}
get getUsername() {
return this.username;
}
get getUserColor() {
return this.color;
}
}
const userStore = new UserStore();
export default userStore;

App.js with Provider

In the root file App.js we provide the stores to children nodes

import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { Provider, observer } from 'mobx-react';
import { UserStore } from './stores'
import RootComponent from './RootComponent';
import { isSynchronized } from 'mobx-persist-store';
const stores = { UserStore };const App = observer(() => {
return (
<Provider {...stores}>
<Router>
<Switch>
<Route path='/' exact component={RootComponent} />
</Switch>
</Router>
</Provider>
);
})

Usage in the store and observables
We bring the usage UserStore in componentC:

import React, { Component } from 'react'
import { inject, observer } from 'mobx-react'
const ComponentC = inject("UserStore")(
observer(
class ComponentC extends Component {
render() {
return (
<div>
<p>{`Component B`}</p>
<button onClick={() => this.props.UserStore.setUser('some text')}>Click to set user/button>
</div>
)
}
}
)
)
export default ComponentC

Save in local storage (Persist)

We want a way to save data to local storage and use it
In the last MobX versions we use in mobx-persistbut the package not support in MobX 6 and not updated for 3 years (from today -2021)
So, we moved to another package mobx-persist-store

We install the package

yarn add mobx-persist-store

Now we need to add a few things to each store:
Add persistStore(this, [‘username’], ‘UserStore’) to store contractor (the second parameter is an array of fields that you want to save in local storage.
persistStore this is an external function:

import { persistence, StorageAdapter } from "mobx-persist-store";export const persistStore = (target, properties, persistName) => {
// If you are using Next.js or server side rendering
// const isServer = typeof window === "undefined";
// if (isServer) {
// return target;
// }
return persistence({
name: persistName,
properties,
adapter: new StorageAdapter({
read: async (name) => {
const data = window.localStorage.getItem(name);
return data ? JSON.parse(data) : undefined;
},
write: async (name, content) => {
window.localStorage.setItem(name, JSON.stringify(content));
}
}),
reactionOptions: {
delay: 200
}
})(target);
};

Hydrate local storage data

The last thing — the hydrate of local storage data is automatically and our job is to catch when it finishes to hydrate
We update App.js file (with multiple stores):

import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { Provider, observer } from 'mobx-react';
import { GameStore, UserStore } from './stores'
import RootComponent from './RootComponent';
import { isSynchronized } from 'mobx-persist-store';
const stores = { GameStore, UserStore };const App = observer(() => { const allStoreAreSynchronized = () => {
return Object.values(stores).every((store => {
return isSynchronized(store)
}))
}
if (!allStoreAreSynchronized()) {
console.log("loading")
return <p>Loading...</p>;
}
return (
<Provider {...stores}>
<Router>
<Switch>
<Route path='/' exact component={RootComponent} />
</Switch>
</Router>
</Provider>
);
})
export default App;

That’s it! you can use Mobx 6 with persist 🚀

You can see more options and details in the source code:
GitHub project: https://github.com/idanlevi1/mobx6-with-persist-starter
Codesandbox: https://codesandbox.io/s/mobx6-with-persist-starter-uq818?file=/src/App.js:0-840

--

--