MobX Store with React JS (Another powerful store like Redux) | 2023

Amir Mustafa
9 min readMar 19, 2023

--

→ MobX is a state management tool. This is another store like Redux.

→ The code is less complicated yet powerful. There are three terminologies:

1. State — Data that drives the application. The MobX store state.

We mark a property as observable and MobX is smart enough to know whenever the state value is updated. Dependent.

2. Actions — Any piece of code that changes the state

3. Derivation — Anything that is derived from the state without any further interaction

Installing MobX Chrome Plugin:

→ Now like Redux we have a chrome extension where we can view the MobX store state data. We will use it in our project below.

What are we creating?

→ A Todo app that fetches data from the MobX store instead of React state

→ We will do CRUD operation with MobX state.

Installing React JS:

STEP 1: Let us install React app using below command:

npx create-react-app –template mobx-react-todo

STEP 2: Install mobx and mobx-react-lite

npm install mobx mobx-react-lite

→ We have created inside components two folders. We can refer complete code here.

File: src/components/Home.js

import React from 'react';

function Home({ store }) {
return (
<div className="App">
<h1>MobX 2</h1>
</div>
);
}

export default Home

→ Now two empty files are created — About.js and UserStore.js

→ In UserStore we will write MobX complete code (which is our store)

Browser:

→ The next step is to write in UserStore.js

File: src/UserStore.js

import { action, computed, makeObservable, observable, autorun, runInAction } from 'mobx';

// DEFAULT STATE
class UserStore {
userInfo = {
id: 'C117',
name: 'code',
subject: ['English', 'CS', 'Maths']
}

constructor() {
makeObservable(this, {
userInfo: observable,
totalSubject: computed,
updateUser: action,
addSubject: action
});
autorun(this.logUserDetails); // WILL RUN AFTER EVERY ACTION INVOKE
runInAction(this.prefetchData);
}

// All getters behave as computed i.e. instantly calculate without action
get totalSubject() {
return this.userInfo.subject.length;
}

logUserDetails = () => {
console.log(`Subject length: ${this.totalSubject()}`);
}

updateUser = (name) => {
return 'NA';
}

addSubject = (data) => {
return 'NA';
}


prefetchData = () => {
console.log('printing prefetchData...');
}
}
export default UserStore;

File: src/App.js

import './App.css';
import Home from './components/Home';
import UserStore from './UserStore';

function App() {
const store = new UserStore();
return (
<Home store={store} />
);
}

export default App;

→ Now to use store in the component we will make use of mobx-react-lite library.

File: src/components/Home.js

import React from 'react';
import { observer } from "mobx-react-lite"

function Home({ store }) {
return (
<div className="App">
<h1>MobX Store</h1>
<h1>{store.userInfo.name} - {store.userInfo.id}</h1>
</div>
);
}

export default observer(Home);

→ Hurray, we have fetched our first store data i.e. userInfo name and id 😀

Order in which consoles print:

1st — all getters in the MobX store will print

2nd — autorun() runs

3rd — runInAction runs

File: src/UserStore.js

import { action, computed, makeObservable, observable, autorun, runInAction } from 'mobx';

class UserStore {
userInfo = {
id: '113',
name: 'Happy Learnings',
subject: ['English', 'CS', 'Maths']
}

constructor() {
makeObservable(this, {
userInfo: observable,
totalSubject: computed,
updateUser: action,
addSubject: action
});
autorun(this.logUserDetails);
runInAction(this.prefetchData);
}

get totalSubject() {
console.log(`getter`); // ADDED CONSOLE
return this.userInfo.subject.length;
}

logUserDetails = () => {
console.log(`Subject length: ${this.totalSubject}`); // ADDED CONSOLE
}

updateUser = (name) => {
return 'NA';
}

addSubject = (data) => {
return 'NA';
}

prefetchData = () => {
console.log('run in action...'); // ADDED CONSOLE
}
}
export default UserStore;

Browser:

→ We observe first getters prints, then autoRun and finally runInAction

Updating MobX State:

→ Let us create a button to update the state.

→ For Any state we need to update we have to go through the action route.

→ We will create a button and use this store method to update the state.

File: src/UserStore.js

import { action, computed, makeObservable, observable, autorun, runInAction } from 'mobx';
class UserStore {
userInfo = {
id: '113',
name: 'Happy Learnings',
subject: ['English', 'CS', 'Maths']
}
// NOTE: MakeObservable, autorun and runInAction are written in constructor
constructor() {
makeObservable(this, {
userInfo: observable,
totalSubject: computed,
updateUser: action, // MOBX SHOULD KNOW - UPDATE STATE
addSubject: action
});
autorun(this.logUserDetails);
runInAction(this.prefetchData);
}
get totalSubject() {
console.log(`getter`);
return this.userInfo.subject.length;
}
logUserDetails = () => {
console.log(`Subject length: ${this.totalSubject}`);
}
updateUser = (name) => {
this.userInfo.name = name; // UPDATE STATE CODE
}
addSubject = (data) => {
return 'NA';
}
prefetchData = () => {
console.log('run in action...');
}
}
export default UserStore;

→ Let us now create a button inside the Home component to update this state.

File: src/components/Home.js

import React from 'react';
import { observer } from "mobx-react-lite"
function Home({ store }) {
const changeUser = () => { // FUNCTION CREATE
store.updateUser("New data"); // USING STORE METHOD TO UPDATE
}
return (
<div className="App">
<h1>MobX Store</h1>
<h1>{store.userInfo.name} - {store.userInfo.id}</h1>
<button onClick={changeUser}>Update User</button>
</div>
); // ABOVE BUTTON CREATED TO UPDATE STATE
}
export default observer(Home);

→ All the code is written in Home component, it is important we pass the store from App

→ We observe from above code Home receives a store.

File: src/App.js

import './App.css';
import Home from './components/Home';
import UserStore from './UserStore'; // FETCHING USER STORE
function App() {
const store = new UserStore(); // INSTANCE IS PASSED IN HOME
return (
<>
<Home store={store} /> // PASSING STORE to HOME
</>
);
}
export default App;

NOTE:

In MobX, instance of Store is passed to components

Browser:

→ We observe Name is changed.

→ Now let us add subjects and on the homepage loop through subjects.

File: src/components/Home.js

import React from 'react';
import { observer } from "mobx-react-lite"

function Home({ store }) {
const changeUser = () => {
store.updateUser("New data");
}

const addSubject = () => { // ADD SUBJECT METHOD
store.addSubject('Aeronotics');
}
return (
<div className="App">
<h1>MobX Store</h1>
<h1>{store.userInfo.name} - {store.userInfo.id}</h1>
<button onClick={changeUser}>Update User</button>
<button onClick={addSubject}>Add Subject</button>
{ // LOOPING SBJECTS ARRAY PRINING HERE, ADD SUB BUTTON CREATED
store.userInfo.subject.map((key, index) => <p key={index}>{key}</p>)
}
</div>
);
}

export default observer(Home);

→ In the store this method will push in the aray

File: src/UserStore.js

Note: Any function we want to create must be done in Store class only

import { action, computed, makeObservable, observable, autorun, runInAction } from 'mobx';

class UserStore {
userInfo = {
id: '113',
name: 'Happy Learnings',
subject: ['English', 'CS', 'Maths'] // SUBJECT ARRAY
}

constructor() {
makeObservable(this, {
userInfo: observable,
totalSubject: computed,
updateUser: action,
addSubject: action // REGISTER IN MOBX
});
autorun(this.logUserDetails);
runInAction(this.prefetchData);
}

get totalSubject() {
console.log(`getter`);
return this.userInfo.subject.length;
}

logUserDetails = () => {
console.log(`Subject length: ${this.totalSubject}`);
}

updateUser = (name) => {
this.userInfo.name = name;
}

addSubject = (data) => { // METHOD TO PUSH IN SUBJECT ARRAY
this.userInfo.subject.push(data);
}

prefetchData = () => {
console.log('run in action...');
}
}
export default UserStore;

→ We will now parallelly check the chrome MobX store to observe state.

→ Open the developer tool in chrome. Just like Network, and Console, there is MobX as well (comes from chrome extension we have installed above).

→ On clicking update user — updateUser method in class invokes.

→ We observe MobX has detected data. We can see the latest store value here.

→ Let us now click Add subject button — hard coded at present (Aeronotics).

File: src/components/Home.js (as we have seen in above code)

const addSubject = () => {
store.addSubject('Aeronotics');
}

→ Let click Add subject

→ We observe MobX store detected state changes. Click on addSubject on MobX

→ We see Aeronautics is added in the store array

→ It is also reflected in the browser as shown above.

Note: One exciting thing:

We observe logs in the browser are printing when addSubject is called and not updateUser for this

File: src/UserStore.js

import { action, computed, makeObservable, observable, autorun, runInAction } from 'mobx';

class UserStore {
userInfo = {
id: '113',
name: 'Happy Learnings',
subject: ['English', 'CS', 'Maths']
}

constructor() {
makeObservable(this, {
userInfo: observable,
totalSubject: computed,
updateUser: action,
addSubject: action
});
autorun(this.logUserDetails);
runInAction(this.prefetchData);
}

get totalSubject() {
console.log(`getter`);
return this.userInfo.subject.length;
}

logUserDetails = () => {
// Added Name in the Log. SO will this method will also invoke in name change
console.log(`Subject length: ${this.totalSubject}, Name: ${this.userInfo.name}`);
}



}
export default UserStore;

Passing to the same Store to More than one Component:

→ Let us now write some code in About.js file

Path: Path: src/components/About.js (New component)

import { observer } from "mobx-react-lite";
function About({ store }) { // RECEIVES STORE FROM APP FILE
return (
<div className="App">
<h1>About - {store.userInfo.name}</h1>
</div>
);
}
export default observer(About);

File: src/App.js

import './App.css';
import Home from './components/Home';
import About from './components/About';
import UserStore from './UserStore';

function App() {
const store = new UserStore();
return (
<>
<Home store={store} />
<About store={store} /> PASSING STORE IN ABOUT COMPONENT
</>
);
}

export default App;

Github:

https://github.com/AmirMustafa/react-mobx-todo/tree/mobx

Video:

Closing Thoughts:

In this article, we have implemented the MobX store with the React.js application. MobX is state management just like Redux.

MobX can be used with heavy projects. The complexity of writing MobX code is very less compared to the Redux store and it is fast in the process.

Thank you for reading till the end 🙌 . If you enjoyed this article or learned something new, support me by clicking the share button below to reach more people and/or give me a follow on Twitter and subscribe Happy Learnings !! to see some other tips, articles, and things I learn about and share there.

--

--

Amir Mustafa

JavaScript Specialist | Consultant | YouTuber 🎬. | AWS ☁️ | Docker 🐳 | Digital Nomad | Human. Connect with me on https://www.linkedin.com/in/amirmustafa1/