Skip to content

Replace Models and Collections with Redux

JC Brand edited this page Aug 30, 2020 · 5 revisions

Currently the Converse state is represented by Backbone/Skeletor Models and Collections, which are implemented in an object oriented way. What this means is that methods for updating the state, as well as methods that implement parts of the XMPP protocol relevant to a part of the state (so-called "business logic"), reside on the Model and Collection classes themselves.

For brevity I'll continue writing only "Models", even though "Collections" are also included.

As is common in object oriented code-bases, we therefore have a mixture of state and business logic grouped together in our objects.

To move to a more functional approach, where we use Redux as a global store, we need to separate the business logic from the Models, so that they are simply "dumb" state containers, with no controller logic. This will then later enable us to keep all our existing business logic, while replacing all the models with a Redux store.

Here's a proposal on how we can do this:

  • We create a "store" abstraction, which mimics the one from Redux and does the following:
    • encapsulates the models, so that we don't have to deal with them directly
    • provides a dispatch function with which components can dispatch actions
    • provides a way to get the state as a tree structure
  • We update the components to dispatch actions instead of directly calling methods on the model instances.
  • For each Model (e.g. ChatBox, ChatRoomOccupant etc.) in each plugin, we extract out all the business logic into functions in a separate, plugin-specific file called controller.js.
  • We then also add to controller.js the handlers for dispatched actions from our components. In Redux these would be reducers, but since our store isn't immutable, our handlers will likely not be actual reducer functions.
  • We create a connect() wrapper function, similar to the one from react-redux or pwa-helpers, which listens for changes to the store (which in our case is just an abstraction of the interconnected model instances).
  • Our top-level component uses the connect function to be informed when any model has changed. It receives the state tree, and passes it down as props to its child components.
  • We remove from our components any references to Model instances, and any event listeners on models. Instead, components will be as "dumb" as possible and only receive props from their containers.

Once all the above steps have been implemented, we will already be implementing a Redux-like architecture, without actually using Redux, and it should be relatively simple (ha!) to completely remove the Models and replace them with Redux.

It's not intended that these above steps be implemented in sequence. Instead, we would conceivably have a mixture of object oriented and redux-like code for quite a while, as we piece-by-piece extract business logic from our models and put them in controller.js, similarly with dispatching actions from the components.