Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
🔥 This is not a real PR 🔥
Its intent is facilitate a discussion around redux, and what that would look like.
Motivation
Kaare and I have discussed this and we are of the opinion that mobx is a bunch of trendy voodoo bullcrap. Among its issues:
It's difficult to debug (need to require
toJS
everywhere).Mystery proxy objects have baked-in side effects, are not available in any version of IE, cannot be shimmed. We can say "who cares" about this and that's fine right up until it isn't and then we are screwed on a very fundamenal level.
It does not work properly with async/await without more mystery mobx baked beans sprinkled on top. Seriously, take a look at their "nicer approach" documentation and tell me if it makes any sense to you at all, let alone a junior dev or someone jumping in for the first time.
Depending on what you "dot into" (ie depending on whether or not you call
store.items
or not) may or may not cause a re-render which I find to be very opaque dark magic BS. Took quite a while to track down before I realized what it was doing.Redux Pros
Redux is also somewhat hipster, being based on FP and immutability principles. However it turns out that there is an underlying reason for this and some big advantages it gets you:
State is nothing but simple Javascript objects (easier to debug).
State is also serializable. This restriction is occasionally tricky, however it unlocks some very powerful concepts:
The biggest advantage is better debugging. Every action performed in an app leads to a change in global state, which is essentially a snapshot in time. State can be rewound, fast-forwarded, or jumped to to pinpoint issues.
As an ultimate goal, state can even be captured and sent along in bug reports to be "played back" later. IMHO this is something really interesting for tracking down production bugs.
Undo/redo are trivial as you can simply pop/push state off an array.
Updates are fast as once state is derived objects are simply compared by reference with
===
to determine if changes took place.Testing is potentially a lot easier as you can simply throw a state at a store/component, fire an action and check the derived state, making things easier to reason about.
Caching API requests can be easy with patterns like
lastUpdated
on a call that can return cached data depending on what the app determines to be "fresh".There is a concept of a "pipe" and "middleware" similar to Koa. This can be used for logging, caching (as above) or other interesting things.
It's actually a very tiny library.
Redux Cons
Redux is at full power when everything is in the global state including UI. This might require a bit of a paradigm shift, and I'm not suggesting we do this. I think a lot of the complexity can be abstracted away and it is possible to keep only our business data in the stores and still benefit, however it isn't using the full power.
How to try this out
The easiest way to see what's going on here is to install the Redux DevTools extension, run the server, go to
/shops
and check out theRedux
tab in the inspector. You can jump between state and get a sense of what's going on. Note that only that page is hooked up and probably none of the modals work yet, etc.Approach
The big work here is in
utils/store/index.js
. It's complicated but there's some tapdancing going on to keep our code similar to the way it is now (injecting stores, etc). Of note:Currently we assume only a single
items
store for each endpoint, but this is problematic as it contains meta information like order. Similarly,sort
,limit
,skip
etc are assumed to be global, however different components may require different "slices" of data on the same page. As an attempt to solve this I've made each injected store a separate instance (not a global singleton). This has its own issues and I don't like it, but as a test this is how I've set it up.Redux is the only dependency. There are a number of supporting libs that seem good (especially
react-redux
), but for a proof of concept I wanted to see what we can achieve with just redux. I don't mind using certain helper libs but I don't want to use some small unknown library that will never get updated and become the weak link in the system.As recommended best practice, data is "normalized" like a DB, so similar to the way we had there is a "register" and the resulting arrays keep only IDs as references.
Each store instance has helper functions like
.loading
etc. I would like to get to a point where we can write something like this:Not sure what you guys think about this but it's what I've been working toward.
TBD
Our current setup has an issue where if a user navigates away while an API call is in-flight, when it lands it will try to
setState
on an umounted component. I've seen some Sentry errors around this. Redux takes care of this out of the box by usingsubscribe
andunsubscribe
, but separate to what we use for store/state here it might be good to think about aborting requests.Handling state updates for POST/PATCH/DELETE. Can we do this with minimal invasiveness?