Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

So mobx is basically global state, and an efficient change tracking algo that decides which parts of the tree need to be re-rendered.

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.



Mobx is not a global state, you can create your stores as granular as you want them to.

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)


The way that mobx is advised to be used, that is/was documented by its author, promotes the usage of a rootStore, which has as members all the various granular store objects. Is this how your organisation's stores are constructed?


We used to have that but moved to an approach with multiple "rootStores" per self-contained route (sometimes we have more than 1 root store in such a scenario).

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.


In my experience, not specific to front end, managing multiple long life objects can be a headache.

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?


(Thinking in terms of local stores)

> 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))`

[1]: https://mobx.js.org/react-integration.html


To be honest I don't understand why people use mobx this way. I mean short life store objects, populated by props, that are recreated every mount. Might as well just use React state and save the overhead. You might say, because I have several components using the same business logic and fields and such, I can inject the same store into multiple components. I would say that logic can go in functions which take React state as their argument. You don't need a global state management tool to separate business logic from the view.


> Might as well just use React state and save the overhead. Unless you mean conceptual overhead (i.e another library), it should be less overhead with mobx because there's no need to re-create objects on every re-render, and further re-renders in general are less common (since mobx precisely controls renders for you). Also localStore's need not be short lived by definition, just local. I have a notes app where the main components localStore stays mounted for (literally) days :)


It's more overhead in the sense that mobx needs to walk all your store objects and check for changes, on top of what React does. Anyway that's fine if you really need that precision. I just useMemo at the 'hotspots' and that is enough. Remember also that if vdom diffing doesn't pick up a change, nothing is flushed out to the DOM, so whether an object is created or long-life doesn't matter in this sense.


> mobx needs to walk all your store objects and check for changes, on top of what React does.

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.


Most of the stores not long-life objects.

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)


no, do you have source for that? Mobx is built on the concept of atom which you can put anywhere in your app, be it a module, a context or a component.



I believe having application state defined independently of the UI components is very helpful in separating concerns. I can debug the state of the application as data only, without needing to dive in to the UI components hierarchy. UI is just a representation of that state and there can be more than one such representation. Singleton isn't bad if it is used only for reading the data. Mutable global state is the problem.


I think you are essentially talking about unit testing your stores, and in the case where there might be a lot of logic inside them, is a good idea in principal.

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.


You mention redux in your original comment and I wouldn't call redux store an imperative object. You have pure functions (reducers) updating a state, which is just a simple object without methods. The benefits I see in using this one application state is that I can easily debug the state of the application, I can easily serialize the whole state and include it in a crash report and the devtools for redux are very helpful. Although YMMV


> Singleton isn't bad if it is used only for reading the data. Mutable global state is the problem.

If it’s read-only, you can just declare it as a constant. No need to add a state management library.


Read-only != constant. State management library can provide you with globally readable state represented by stream of immutable values.


I have come mostly to the same conclusion. UI specific state (eg a flag to store whether the left panel is folded) should be as local as possible.

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.


UI specific state (eg a flag to store whether the left panel is folded) should be as local as possible

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.


In my opinion, the sharing of state in the manner you described leads to implicit coupling between components, which can be hard to hold in your head as the application grows.


I would always forget to put the observable decorator, then wonder why there was not a re-render. This situation occurred so many times I lost count. It's not mobx, it's me that is the problem.


Maybe you'll like this update then -- check out `makeAutoObservable`, it automatically picks sensible defaults to make observable


What do you do with state that needs to be global, or state that bits of start needing to be available in many components for example current username or permissions etc?


This is not a religious stand against all global state. Username, permissions fine to be global, this is very easy for someone new to your code to grok. I put them in the top React context, then just useReducer where I need to. Don't need to pass down through props to every component anymore, thanks to React context and useReducer.

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.


React Context is the use case for that one.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: