Integrating Preact with MobX State Tree

Last updated: 22 days ago

Published: almost 2 years ago

raw source | baked source

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:

1"dependencies": {
2    "mobx": "^6.9.0",
3    "mobx-preact": "^3.0.0",
4    "mobx-state-tree": "^5.1.8",
5    "preact": "^10.13.2"
6},

Setting up the store

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

1import { types } from "mobx-state-tree";
2
3const RootStore = types.model("RootStore", {
4  // ...
5}).actions(self => {
6  // ...
7})
8
9export 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

1import { createContext } from "preact";
2
3const RootStoreContext = createContext();
4export const StoreProvider = ({ children }) => (
5  <RootStoreContext.Provider value={store}>{children}</RootStoreContext.Provider>
6)

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.

1import { useContext } from "preact/compat"; // preact/compat, not preact-compat!
2
3export function useStore() {
4  const store = useContext(RootStoreContext);
5  if (!store) {
6    throw new Error("Store cannot be null, please add a context provider");
7  }
8  return store;
9}

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

1const App = () => (
2  <StoreProvider>
3    <h1>Preact + MobX State Tree example</h1>
4    <Todos />
5  </StoreProvider>
6)
7
8render(<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

 1import {observer} from "mobx-preact";
 2
 3export const TodoList = observer(() => {
 4  const { todos, deleteTodo } = useStore();
 5
 6  return (
 7    <ul>
 8      {todos.map((todo, ix) => <Todo todo={todo} deleteTodo={() => deleteTodo(ix)} />)}
 9    </ul>
10  )
11})

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.

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


  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. [return]

Comments (0)
Add a comment