Type something to search...
MobX has all the state management I need

MobX has all the state management I need

Why I think about MobX

During the years I came across multiple state management tools. Vuex, Pinia, Redux, MobX, Zustand and maybe more. I remember how overwhelming this topic was when I started. Redux, with all the boilerplate. What is a store, what is an action, what is a reducer, what is a selector, what is a thunk, what is an effect and there was middleware as well. Of course you had to configure all of this yourself as well The true opposite to redux was Pinia from the vue ecosystem Pinia is a very simple. You just define a store and you can use it. Stores, getters, actions. That’s it.

Zustand reminds me a bit of Pinia. The simplicity is nice. But I always had the feeling that the simplicty is always a bit of a trade-off. You have to think more about when to use which function. Is this state stable, can I use it in a computed property? And that’s why sometimes it can become a bit tricky to use in react projects with lots of components.

I feel like the reason to use Pinia or Zustand is that that they allow you to quickly use a global store. This is pretty nice for small projects, but if the application is large or your small application starts growing, this can become a burden. You need more structure. And here comes MobX into play.

Why I use MobX for state management

I almost said it now, but hear me out. There is one reason above all why I choose MobX over any other state management tool. MobX is so simple to use and yet so powerful.

In my 9-5 job, I learned about MobX and used it for a couple of years. Although in the beginning it was a bit of a black box, the more I used it the more I appreciated its simplicity and power.

The most important lesson I had was that it simply works. It does not matter how small or big your project is, MobX can do the job well. The overhead is not too big for a small project. It might be more code to write as with Pinia or Zustand, but you have a beautiful clean codebase. Even if you tend to write a lot of logic in your actions (fat stores), it will be fine. And if we are honest, writing a lot of code is no problem for all of you LLM vibe coders anyway!

I use MobX in all my projects, because its a great tool and it is great if you know one tool well that you use in all your projects. That reduces the mental load significantly. That’s why I recommend to you, to just do the same.

Root Store Pattern with Hooks in React

Here is the root store pattern that I use in all my projects. I recommend to use it with a hook. This way you can simply use it in your components and use destructuring to access “child stores”.

The Mobx Stores

For demonstration purposes, let’s define two small stores for cakes and doughnuts.

What are the key takeaways here:

  • count is a property that will be observed by mobx. I.e. whenever it changes, the components that use it will re-render.
  • We use makeAutoObservable(this, { rootStore: false }); to make the store observable. The second argument is to exclude properties from being observed. This way we prevent infinite recursion, because we don’t want that our child stores react on changes within the rootStore. (Did you get that?)

I usually name my stores like {module-name}.store.ts. If we follow this pattern, we would now create cake.store.ts and doughnut.store.ts.


import { makeAutoObservable } from "mobx";

export class CakeStore {
  count = 10;
  rootStore: RootStore;

  constructor(rootStore: RootStore) {
    makeAutoObservable(this, { rootStore: false });
    this.rootStore = rootStore;
  }

  bakeCake = () => {
    this.count++;
  };
}

export class DoughnutStore {
  count = 20;
  rootStore: RootStore;

  constructor(rootStore: RootStore) {
    makeAutoObservable(this, { rootStore: false });
    this.rootStore = rootStore;
  }

  glazeDoughnut = () => {
    this.count++;
  };
}

MobX RootStore

After we have created our stores, we can create a root store. The root store will instanciate the child stores. This main store will also be the store that we will pass to our components, or better said, we will create a hook that will return the root store.

export class RootStore {
  cakeStore: CakeStore;
  doughnutStore: DoughnutStore;

  constructor() {
    // here we can instanciate all our stores, and we pass a reference of the rootStore to the child stores.
    this.cakeStore = new CakeStore(this);
    this.doughnutStore = new DoughnutStore(this);
  }
}

// we return the rootStore as instanciated singleton
export const rootStore = new RootStore();

Implementing the MobX stores into your React Application

Perfect, we have a root store! Now we want to use this store in our app. The recommended way is to use a hook, but there is one more thing you should add. We have to create a provider for our root store. And we have to wrap our application in this provider.

So we will create the context, create the provider and create the hook that will be used to access the store.

"use client"; // required if using Next.js App Router

import React, { createContext, useContext, useMemo } from "react";
import { RootStore } from "./RootStore";

const StoreContext = createContext<RootStore | undefined>(undefined);

export const StoreProvider: React.FC<{ children: React.ReactNode; initialData?: any }> = ({ 
  children, 
  initialData 
}) => {
  // create a fresh store per request/mount
  const store = useMemo(() => {
    const s = new RootStore();
    // if we have data from the server, we "hydrate" it here 
    // this method does not exist in our example yet, but it is a important feature to know
    if (initialData) {
      s.hydrate(initialData);
    }
    return s;
  }, [initialData]);

  return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
};

Why is the provider necessary? If you build a single page applictation, you might not need it. But if you have a multi page application or you do SSR, it is necessary to wrap your application in a provider. The singleton will remain in the memory. If User A visits your site and changes the count property in the cake store to 500, that value stays in memory. When User B visits the site, they will see User A’s 500 cakes because they are both hitting the same shared singleton on the server.

The fix here is that we will use a provider that will make sure, on every request a new store instance is created.

import { StoreProvider } from "./StoreContext";

const App = () => (
  <StoreProvider>
    <BakeryStats />
  </StoreProvider>
);

And this is how the MobX root store pattern with context provider looks like when it’s used in a component.


import React from "react";
import { observer } from "mobx-react-lite";
import { useRootStore } from "./StoreContext";

const BakeryStats = observer(() => {
  // get child stores via destructuring
  const { cakeStore, doughnutStore } = useRootStore();

  return (
    <div style={{ padding: "20px", border: "1px solid #ccc" }}>
      <h2>Bakery Dashboard</h2>
      
      <div>
        <p>Cakes in stock: {cakeStore.count}</p>
        <button onClick={cakeStore.bakeCake}>Bake a Cake</button>
      </div>

      <hr />

      <div>
        <p>Doughnuts in stock: {doughnutStore.count}</p>
        <button onClick={doughnutStore.glazeDoughnut}>Glaze a Doughnut</button>
      </div>
    </div>
  );
});

export default BakeryStats;

Related Posts

Appstore and Playstore nightmare

Appstore and Playstore nightmare

Appstore and Playstore Nightmare Releasing apps and seeing the first downloads feels amazing, but the journey to get there is another story. Right now, two of my apps are fully released. One is **

read more
Running Gemma4 local

Running Gemma4 local

Recently Google release Gemma4, the new open ai model which also permits commercial use. The capabilities are very int

read more
Maestro for App Store and Play Store screenshots

Maestro for App Store and Play Store screenshots

Just yesterday, I was creating dozens of screenshots of my app (Kitchen Converter) for the App Store and Play Store. I can tell you... it is a time consuming process to crea

read more
Using oxfmt and oxlint in react-native

Using oxfmt and oxlint in react-native

Warning: This article is more about the technical side of our development work. Oxfmt and Oxlint in react native Since the last weeks we heard more and more about [oxfmt and oxlint](https://oxc.rs

read more
Memorybank - Journal and Diary

Memorybank - Journal and Diary

Memorybank a new Journal and Diary app Since I was traveling a lot in the last years, I had a problem. I wanted to keep track of my memories. I do journaling from time to time. I am not a daily jou

read more