Work smarter to reach a maintainable code. Find out why, when, and how to use mobx-react-lite from a new article of our engineer Bogdan Birdie.
The popularity of MobX is getting higher and higher. So does the need to decrease the size of the bundle. And not only that, but we also seek to write cleaner and maintainable code in our projects. So how can we achieve this?
Since the React team released hooks from v16.8.0 — we were introduced with a new nice way to write the components in a functional way. In my personal opinion — the use of the hooks over the HOC’s looks way cleaner. Accessing the data from the store with hooks also seems cleaner. We tend to write less code with functional components comparing to big chunks of code that we used to write some times with Class-like components. That is one of the ways to increase the maintainability of the code.
What about bundle size?
Using lightweight packages alternatives is a great way to reduce the final bundle size. And we can do that with many packages. Remember using Moment.js? I don’t recall using it for a while because it has a way bigger bundle size that the alternative Date-fns package. Even tho the Moment still has twice more downloads and stars.
Mobx-react-lite is a lightweight binding to glue Mobx stores and functional React components. And the size of it is smaller, but maybe not as much as you would expect. Nevertheless — this is all we need.
Let’s get started
We will need a React project setup and two dependencies to get started:
yarn add mobx mobx-react-liteORnpm install mobx mobx-react-lite
For a simple example, we can create a Counter Store with some actions and values. I’m using Typescript with the feature enabled to support decorators syntax, but it does not matter if you are using decorators or functions for our case.
In order to access the store from the component we will need to create the instance of the store and, of course, share it in some way with components. React Context is a quite good fit for this task and we can leverage it for our needs. Let’s create a stores.ts
file with the store instances and Context wrapper.
Good. Now we have a stores
variable to which we can save instances of the Mobx stores. We freeze the object in order to avoid any unexpected changes in it. But, of course, this is an optional step.
We created a React Context based on the store’s variables, and also created a Stores Provider component, we will use it shortly.
Now, let’s update the root index.tsx
file with a Context Provider wrapper. Follow this code snippet.
Very well. At this point, we have a place, where we keep our stores. We also have a way to share the stores with the components, but not completely. To access that context we have to create two custom and quite handy hooks. One will return all stores, another — specific store of preference.
Alright, now we have it. See that weird type definition for the useStore
hook? It would give us proper types for the passed store key.
<T extends keyof typeof stores>
(store: T): typeof stores[T]
We will only accept store
variable type if it’s one of the keys of the stores
object. So, in our case, it will only accept a counterStore
string and will return the corresponding type of the given store. Nice, isn’t it?
Ok, let’s finally access the store, its methods, and properties from the actual component. To do that we have to modify App.tsx
file with the custom hooks and Mobx Observer wrapper.
We access the counter store with it’s key and get the store in return. Also, we have to wrap the component into the observer
function to allow the component to listen for the store changes. But still, we do not destruct stores.
const counterStore = useStore("counterStore");
The app is working as expected, cool! Let’s see the TypeScript hints that we got from this setup and the ways it will cover our back from doing wrong things.
The first thing to mention is the code completion feature. When we will add the useStore
hook and start passing a string there as an argument — a key hint will be shown. We also will not be able to pass non-existing store keys. Nice!
This also works for the case when we have multiple stores as well. We can access them with a separate useStore
hook or with a useStores
hook as well. Check this out.
Testing
Sure thing we need to test this. In this article, we won’t make tests for the store itself, since this is a topic of a different nature.
I find it easy enough to use Jest alongside with React Testing Library. So, let’s add the necessary dependencies.
yarn add @testing-library/react-hooks react-test-renderer -DORnpm install @testing-library/react-hooks react-test-renderer --save-dev
Cool! Now we can start testing our hooks file. Nothing too fancy here, but still necessary.
How about component tests?
To do that we will need a few more dependencies. To test the components and to perform user events.
yarn add @testing-library/react @testing-library/user-event -DOR
npm install @testing-library/react @testing-library/user-event --save-dev
Good! Let’s test the components now. We can mock the useStore
hook to always return a specific store using Jest. And, we can be type-safe with a few extra lines of code. For every test we want the hook to return a new store. But, for one of the tests, we will change that slightly and replace the initial value of the counter. Check this code snippet.
Quite simple, isn’t it? Yep! And we got a green light as well!
Summary
Mobx-react-lite is actually all I need in my personal projects. Would this be your choice of favor as well? Let me know in the comments below 😉.
The entire project is available on the GitHub link.