Integrating Preact with MobX State Tree

| react frontend preact javascript mobx state tree

I’ve had a whale of a time trying to integrate Preact with MobX state tree lately. I finally succeeded, and here’s how.

tl;dr: here’s the example repo.

Prerequisites

We’ll need to get ourselves MST, MobX itself, Preact of course, and a glue library - mobx-preact. All told, my package.json dependencies section looks like this:

"dependencies": {
    "mobx": "^6.9.0",
    "mobx-preact": "^3.0.0",
    "mobx-state-tree": "^5.1.8",
    "preact": "^10.13.2"
},

Setting up the store

Then, we’ll need to set up a few things. We can begin with our store itself:

import { types } from "mobx-state-tree";

const RootStore = types.model("RootStore", {
  // ...
}).actions(self => {
  // ...
})

export const store = RootStore.create({ ... });

It’s not very important what goes into the store itself, here, so I’m just skipping over these sections. So far, it’s as you’d expect - bog-standard MST root store.

Time for some magic.

Creating a store provider

import { createContext } from "preact";

const RootStoreContext = createContext();
export const StoreProvider = ({ children }) => (
  <RootStoreContext.Provider value={store}>{children}</RootStoreContext.Provider>
)

We use createContext to build a blank context. With that, we build a context1 which will eventually have our store in it. Contexts are a way to pass information deep into our app without having to pass it via props to every single component.

Creating a store “hook”

It’s not really a hook. It’s just a function that starts with use.

import { useContext } from "preact/compat"; // preact/compat, not preact-compat!

export function useStore() {
  const store = useContext(RootStoreContext);
  if (!store) {
    throw new Error("Store cannot be null, please add a context provider");
  }
  return store;
}

This references our context so we can fish out the value of store wherever we choose in our application.

Wrapping our app with the store provider

const App = () => (
  <StoreProvider>
    <h1>Preact + MobX State Tree example</h1>
    <Todos />
  </StoreProvider>
)

render(<App />, document.getElementById('app'))

Here’s the important bit. We import our StoreProvider. Everything that’s beneath it - in this case, our entire application - will have access to the context, and consequently be able to useStore.

Using the store

import {observer} from "mobx-preact";

export const TodoList = observer(() => {
  const { todos, deleteTodo } = useStore();

  return (
    <ul>
      {todos.map((todo, ix) => <Todo todo={todo} deleteTodo={() => deleteTodo(ix)} />)}
    </ul>
  )
})

The only catch we need to look out for is the observer() function wrapping our component. I’m told these can also be used as decorators, but from a cursory reading it looks like it’s for class components only? I don’t really know. I’m not a frontend person 😅

In any case, we can useStore here to access bits and pieces of our store - properly destructured, of course.

In action

Action video? Action video. Here is my example “todo app”, excerpts of which we reviewed above.

You can find the entire thing on my GitHub at paweljw/preact-mst-todo.


Hero image by AndreasAux from Pixabay


  1. Preact contexts work pretty much the same as React’s version. More information can be had at https://react.dev/learn/passing-data-deeply-with-context↩︎

Built with ❤ by Paweł J. Wal in 2023. Hugo helped.

Blog contents, except where otherwise noted, are CC BY-SA 4.0. Code of this blog is MIT.

Toggle dark/light mode