Photo by Karsten Würth (@inf1783) on Unsplash
I started my journey in the Frameworks fairyland with Vue and this amazing series by Laracast. I had heard about React, but back then when I looked at JSX it seemed like an abomination to me! Mixing html tags with JS logic? Haven’t they heard about design patterns and separation of concerns? Why not just use JQuery to make some blinking text for goodness’ sake?
But then I learned JS, not _JQuery JS* (_making tricks with the DOM), but JS as a development language, an elegant expressive tool to manipulate data, a language with beautiful methods to work with Arrays, String, and Objects. And what you can’t do with vanilla JS very likely there’s a Lodash for that, or for sure there’s an NPM package. (npm install make-me-a-sandwich
Lol!)
After all that I took another look at React and… I instantly fell in love! Where Vue is sort of like html with added functionality (v-if, v-for...
) JSX is plain JS that happens to generate DOM. I’m not actually trying to start a frameworks war here. I currently have Vue and React apps on production and I have been able to find all the tools that I need on both frameworks to do my job. The choice really comes down to a personal preference or your company’s/team’s preference.
For a beginner in React it seemed like Redux is the default store as is Vuex for Vue thus I decided to learn Redux… and after watching the complete egghead series about 3 times and going over the documentation just as many times, I still felt like I had no clue what I was doing. Anything less than a trivial reducer was a headache to write. Normalized data, relational tables, immutability and DB technology, they all seemed like pre-requirements for Redux! I did learn a lot, but not enough to feel anywhere close to productive with it. Then there are wrappers for Redux and high order functions to generate other functions and reducer recipes… I felt like an intern again, not being able to finish my project because I didn’t know how to have a simple store and make my app react to it! 😥
I’m sure that there are a lot of developers out there smarter than I am that are very productive with with Redux. I look up to you, Sensei!
I missed the days of Vuex in which everything just worked, mutable state was not a sin and no matter where I called a store action, the whole app was always in sync. But i liked React a lot too so I started to see if I could use Vuex on React, (apparently not) but in the search I came across MobX. It sure had bold claims:
MobX makes state management simple again by addressing the root issue: it makes it impossible to produce an inconsistent state. *
Again I headed to egghead to watch the video tutorials. They were nice but tough luck! create-react-app
does not support decorators and you don’t get a lot of examples of how to do things without them. Then there’s mobx-state-tree
which claims to be opinionated (and that’s an understatement!) and then again, I had no clue how to do things or what was going on! The videos taught me how to make a pretty decent counter and mobx-state-tree
felt like I was writing an enterprise banking application, but I couldn’t find a medium difficulty level.
I really liked React so I went back to Redux but couldn’t make it work so, again, I reluctantly gave MobX another try... “And at last I see the light; And it’s like the fog has lifted”
Shameless Disney plug!
Want I want to do us show you that MobX is easier to use that what you might think. We are gonna use MobX to do more than just count up and down and is simpler than a mobx-state-tree
implementation.
We are going to make a simple store, extracted from a project I’m currently working on. In it we are going to have a list of students and a list of courses, and we will be able to enroll and unenroll students from a course.
The starting point is a clean create-react-app
app and here is store.js
:
🙄 Don’t let the length scare you, it’s actually pretty straight forward the students’ and courses’ methods are the same.
In MobX terminology, there are observables, stuff that is watched for changes, and observers, stuff that watches for changes. From there the rest is magic pretty magical!
First we simply declare const store
as the first parameter of observable()
. *This is how you declare an observable without using the decorator syntax (_@observable_
).
Inside the object passed to observable()
there are three places to put data in: students, courses, and enrollment, and those are defined as observable.map()
. If it’s the first time you have come across a Map, don’t worry, it’s just a fancy object with a nice way to access the contents in it (setters and getters). In a plain JS object data is accessed like this:object.key1.key2
or object['key1']['key2']
, On a map you’d access it like this: object.get('key1').get('key2')
. There are a couple of advantages to this, and in this particular project it saved me from using a lot of _.set()
and _.assign()
(from Lodash).
Then come the store methods to add, update, and remove students and courses from the store. They are just plain .set()
with a some error handling.
The next interesting bit comes in the enrollment handling. This idea definitively has a Redux flavor to it (Relationships and Tables) and it’s useful in this case. I’m making a object with the key being equal to the course’s ID and the value being an array of the enrolled students’ IDs. It looks like this:
{course1: ['student1', 'student2', 'student3'],course2: ['student4', 'student5', 'student6'],}
Where the action happens is in the enrolledStudents
method. Let’s break it down: it returns a computed(() => {...})
function, which is the MobX expression to watch for changes in the elements inside it and to automatically update its value. Nothing fancy there. Next, it gets the element in the enrollment map that matches the ID of the course passed as an argument, which returns an array of the students’ IDs. Then a .map()
through the array substitutes each student’s ID for the whole student object. Note that that last lonely .get()
is applied to computed()
and to get the actual data.Thus the result of enrolledStudents
method is an array of all the students’ objects enrolled in a particular course, sorry, an observable array so any changes to the data inside the function will update it’s return value and do the appropriate re-renders! 🤩
Here’s a simple React component to shows this in practice:
This is a pretty standard React components except for the observer
and the store
.
First, a couple of students and a course are defined and added to the store. Then student1
is enrolled in the course. Next we map through the enrolledStudents
method from the store using the ID of the course for the list of students I want and the results are as expected.
The interesting part happens when you press the button “Enroll Student 2”.In the store, the student ID is added to the array of the corresponding course map, and MobX takes care of keeping it all in sync, correctly compute the value of enrolledStudents
, and doing the appropriate re-renders. And all of that happens because of L39 where we wrap StudentsList
inside observer()
!
Note that your components can get access to the store in a variety of ways. You can import the component from the file just like in the previous example, you can pass it down as a prop from a parent container, or you can use the mobx-react
provider/inject utilities (in my case, I’m fine importing store
whenever I need to).
MobX feels indeed a little magical and when I finally made sense of it it was like a gust of fresh air bursting into my React development! (that’s the reference to the top image). Hope you have fun exploring it and keep making cool, awesome stuff!😎