MobX 6
michel.codes
michel.codes
On top of all that, Michael is a very reasonable and patient project leader. His documentation puts advanced concepts in plain terms, and when people file feature-requests he always displays a genuine willingness to consider their use-cases and points of view, to solve their problem if it's reasonable for the project to do so, and is never condescending even when questions or suggestions are plainly bad.
Just noting that experiences may differ sharply. :)
If you really need the (complex) functionality of MST, redux might be a safer choice going forward in that case.
I admit the only team I've used it on consisted of three people, and there are certainly really gnarly things you can do with it if you go out of the way to, so I don't know how it scales to larger teams. Though I would argue that it encourages best practices by making those the paths of least resistance. I would also say that - unlike, say, RxJS - its mental model is very simple and straightforward, and should be quite accessible to inexperienced developers. But yes, it doesn't do a lot to stop you from shooting yourself in the foot.
> I don't think it really fits well into the React model
I disagree with this part completely. It doesn't fit well with the Redux/hooks model, but that's because it replaces it. In my opinion it lets React focus on what it does best: updating the DOM to match a new virtual DOM, and takes everything else out of React's hands, which to me is a huge win. I am not a believer in the "every state change replaces the entire root state structure using a composition of pure functions" model; I think it's contrived, clunky, and hard to follow. I believe that state should be minimal, but should be treated as what it is. MobX gives you all the tools you need to enshrine state as state, without introducing any duplication/synchronization complications.
I've used both extensively, MobX def wins IMO.
Now of course I moved the code that looks at that state up the tree and then pass it down to solve but that kind of defeats the purpose of using it in the first place. The upper components should not have to care directly about data for lower components.
1) If all 300 photos aren't actually visible, make sure React skips actually rendering them (calling their render functions)
2) If they do all need to be visible, and they do all need to update in real time in response to that preference, consider adding a paging mechanism so that not as many are visible at once
You have to step back to the root question: do you want all of those components to visibly update immediately in response to that change? If not, make sure they don't try to. If yes, MobX is accomplishing that in the most efficient way possible so you may need to rethink your UI.
> I moved the code that looks at that state up the tree and then pass it down to solve
This doesn't really make sense to me as a fix, and to be honest I suspect it's merely covering up the problem in a roundabout way.
isn't it everything? My exp in redux is the same. Mobx however doesn't pretend to be for smart people and doesn't encourage them to invent a bunch of weird conventions like redux. I've never had any problem with state management, react solved it for me already. What I need a library for is a clear pattern for where to put domain code (again is it reducer or saga/thunk?) and never think about the library again.
Compare this with frameworks like Aurelia that bring in piles of new, implicit, inscrutable abstractions in order to accomplish roughly the same thing. Sprawling magic, that you find yourself constantly tripping over left and right.
I loved using React/Inferno + MobX too.
Right now I find myself being much more productive with Svelte which already includes a reactive primitive out of the box (writable/readable) that can be used with classes too.
Svelte has a derived reactive store similar to a computed value in Mobx:
Sure, there are a few gotchas (less so with Proxy object in v5, but there are still some) and you still have to pay some attention to avoiding performance issues, but I think this is true of any alternatives too, and MobX makes 95% of standard React-stateful-app-type dev so easy that I don't mind if there is the odd time where I need to debug something. I think pretty much everyone I've introduced to it has found the same.
As others have alluded to, I feel like often solutions people propose in the world of JS dev (and I'm sure elsewhere) can be overly fussy and complicated, just to achieve some notion of "purity" or whatever. MobX is a pragmatic solution and that aligns with the pragmatic way I generally like to work, so I'm happy to trade off some "magic" for a great developer experience, and I'd really recommend everyone try it at least once!
I've found using React context for state, and judicious usage of useMemo has been enough for me to be able to drop mobx or its kin, even on larger sized projects.
Libraries like mobx and redux implicitly promote a massive singleton state object. I think web applications are easier to maintain if we make every effort to minimise global state. If some component way down on the left hand side of the tree needs to get something to a component at say the top RHS of the tree, I will consider refactoring, rather than add another property to MassiveStateObject. Why? Because when you come back to look at MassiveStateObject in two years the implicit coupling between the two components above will not be clear.
Although I was thinking if you wanted to be a religious zealot about not coding any of your own global state you could use the browser session API.
When you have some functional data (the lisk of tasks in a todo app) then putting that in the React context and injecting it in the UI blocks that need it is the simplest way as long the injection part is well separated (using something like unstated.next).
There is some grey area between the two but with this methodology I have never felt the need to use redux or mobx again.
The last time I had to use mobx I was bitten by the rough edges of the decorators.
MobX is not a replacement for local state. It's a useful mechanism for sharing state between parts of an app that need to share state - in the example of a left panel, if you want to be able to control that from outside of the component then you either need to be able to modify the state (eg MobX), or you need a callback that refers to the local panel state that's callable from outside (eg a Redux action), or you need a method that's passed around the app to where it's needed (eg a React context). If you don't need to control it from outside then you don't need any of these things and useState is fine.
Where MobX is especially useful is in things that aren't simply an observable value that need to be passed around - if you need to transform the value for other components to use it then MobX's @computed and @action are both _incredibly_ helpful.
If it’s read-only, you can just declare it as a constant. No need to add a state management library.
But take the case of an app using local state, this logic if indeed separable from UI, could be coded as functions, rather than within imperative object 'stores'. And these functions would obviously be just as testable.
Each widget can have a store, each section can have a store, each view can have a store, etc. You decicde what works for you.
(I use mobx in a large and complex trading application)
But we also have multiple smaller stores which are not connected to the root store. Think: multiple not-connected trees where each node is a store.
I must agree that both approaches have pros and cons, e.g. passing down data down a long path of stores is annoying.
Out of curiosity, how do your 'not connected' stores know when, say, a user logs out?
Also, I suppose you have to write code, bespoke to each object, to be able to reset these objects too?
> how do your 'not connected' stores know when, say, a user logs out?
If they need this information, it can be passed in from above, or pulled from context. But unless the store and component need to stay mounted when user logs out, they might not need to do anything. There's nothing wrong with a local store being created by a component that receives props and provides them via constructor arguments.
> write code, bespoke to each object, to be able to reset these objects too?
I almost never do this, and nearly always create a new store when the component mounts. The combination of `useLocalStore`[1] with mobx-react-lite makes this straight forward. So per the above example, your component could `useLocalObservable(() => new Store(props.user))`
I don't think that is quite correct. I think it keeps a mapping of observed properties and which React components rely on them. Then, changing any property becomes a lookup (not a walk) of who is observering, and a `forceRender` on the results. So if no components are observing the property, there's no further computation. Its not "on top of what React does" because it short-circuits it (like useMemo) -- except you don't have to hand-craft any useMemo comparators, it does that for you by tracking which properties you access.
The current store holding most of the displayed data stays alive (while not switching to a different route), but everything else resets or gets recreated when needed. (no need to keep stores alive if they are not required or used)
Stores for specific "sub" views (think dialogs, tabs, collapsibles, ...) are getting created/destroyed ad-hoc.
Main Store for a specific view holds the current data (which receives updates via gql subscriptions ~every second), the current filter which is applied to it's children (only show Apple related securities) and currently visible/hidden state of any children.
User/App/Global related attributes which don't change often are stored in a globally available object (and is easy for us as users don't log out or anything)
If I could add a feature to MobX I would add a built-in easy way for serializing and deserializing state to local/sessionStorage without having to reach out for 3rd party library (which aren't even that good). I think that functionality is very vital for any web app.
After that, I guess my biggest issue has been the injection of stores into other stores. It feels a bit dirty and can get messy. Maybe if instead of injection, MobX could offer a context based solution. And then nudge people to build their stores around specific API route/data to make them composable and smaller than the gigantic stores they sometimes come to be. Then you could easily separate the part of the store that is about how to fetch/send data to the API and the part that formats/modifies the data for the UI.
I don't know, maybe that could work. Love MobX either way and hope they keep up the good work!
But doing this is also a huge code smell, and expressly stated as such in the docs, and even though it's necessary every once in a while, it's very possible to keep it under control to where it doesn't become a problem even in quite large projects.
In other words react needs to work with DOM diffing and virtual dom whereas mobx and observables are granular & directly manipulate the values.
This gives mobx in my opinion lots of benefits that are not so apparent in react. Mainly the benefit of performance where a computed property only reruns if any of its dependencies change whereas react updates when any of its props change, even if the prop wasn't used!
A simple example is imagine in react:
props => if (props.a) bar() else if (props.b) baz()
React doesn't care about a nor b and reruns this code whenever props changes.
In mobx (and other observable based systems) if the above computed function runs with prop.a equal to true will make mobx know that "a" needs to be tracked and mobx doesn't yet know about "b" (because that branch has never been evaluated) hence doesn't rerun if b changes.
This kind of reactive granularity is what sets the two apart imho.
solidjs [1] is a framework that works like mobx but for components; hence seems to be the best combination of mobx + react using observables (without virtual dom diffing)
[1]: https://hackernoon.com/introducing-immer-immutability-the-ea...
It looks like a slimmer version of MobX
In terms of concepts it's very similar to MobX but the API is minimal because it's focused on react only.
We're using react-easy-state in the Frontity framework https://github.com/frontity/frontity/ and very happy with it.
(shameless plug warning) I've written about principles behind react-easy-state and MobX here: https://czaplinski.io/blog/make-your-own-mobx/
Edit: What I don't understand with the context API is that it looks like I need to access the instantiated context externally in every component, while MobX injected it through the component tree automatically.
I’m now left wondering what sorts of impending doom the decorator-withdrawal-situation will create for NestJS.
In general I think the TC39 commitee has been doing a great job of moving JS forward, so I'm trusting them to have solid reasons for this call, knowing full well that it's painful in the short term.