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
You can find the entire thing on my GitHub at paweljw/preact-mst-todo.
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.↩︎