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 🚀
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
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-persist
but 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