5 ms·
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 co
by shermanmccoy 6y ago
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.
- andy_ppp 6y agoWhat 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?
- shermanmccoy 6y agoThis 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.
- Cthulhu_ 6y agoReact Context is the use case for that one.
- sbergot 6y agoI 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.
- shermanmccoy 6y agoI 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.
- davedx 6y agoMaybe you'll like this update then -- check out `makeAutoObservable`, it automatically picks sensible defaults to make observable
- onion2k 6y agoUI 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.
- shermanmccoy 6y agoIn 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.
- brulard 6y agoI 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.
- cnasc 6y ago> 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.
- brulard 6y agoRead-only != constant. State management library can provide you with globally readable state represented by stream of immutable values.
- shermanmccoy 6y agoI 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.
- brulard 6y agoYou 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
- hknd 6y agoMobx 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)
- shermanmccoy 6y agoThe 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?
- hknd 6y agoWe 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.
- shermanmccoy 6y agoIn 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?
- cloverich 6y ago(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 https://mobx.js.org/react-integration.html