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
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. ↩︎