Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing out Redux #23

Closed
wants to merge 1 commit into from
Closed

Testing out Redux #23

wants to merge 1 commit into from

Conversation

andrewplummer
Copy link
Collaborator

@andrewplummer andrewplummer commented May 8, 2020

🔥 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 the Redux 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:

componentDidMount() {
  this.props.shops.search({
      // ...filters
  })
}

render() {
    const { shops } = this.props;
    if (shops.loading) {
        return <Loader />;
    } else {
      return shops.map((div) => {
          return (
              <div>
                {shop.name}
              </div>
          )
      }
  }
}

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 using subscribe and unsubscribe, 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?

@andrewplummer
Copy link
Collaborator Author

Going to abandon this one now... redux does have some interesting concepts but for the moment it seems like too much overhead for bedrock at this time. I would like to get into some more advanced use cases for it in other projects first and see how a real-life solution holds up and what that would look like. It could then be brought back into bedrock, probably as a plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant