Reactive Programming in Mobile Applications — a Voyage

Reactdroid — Reactive MVI Architecture for Android— Part 2

A comprehensive mobile development library, designed with a pure Kotlin core and extended with an Android layer. For rapid, structured and solid app development — part 2 — Redux

Guy Michael
Nerd For Tech
Published in
6 min readMar 11, 2021

--

Introduction

In the previous article, we learned How to implement a React-like architecture in Kotlin. This part(s) will continue from where we stopped to show How to implement a Redux-like architecture, using RxKotlin. These two form the Reactdroid library, which is available on GitHub.

This article assumes you have some familiarity with React, Redux and rx.

Redux defines a 1-directional data-flow. Like a rain-forest: water vaporize (dispatch) to the clouds (store) which merge them together and rains (notifies) on everyone :)

How (is the Redux-like architecture built) ?

To remind you — on a high level, this is how Reactdroid’s Kotlin core is structured:

Reactdroid’s Kotlin core

Today, we will talk about Reactdroid’s Redux core and its Store.

What we will cover in this article

  1. The Store class
  2. The Store’s API — subscribe() & dispatch()
  3. The Store’s GlobalState and its StoreKeys
  4. The Store’s Reducers
  5. Connecting our Component to the Store

Next article(s) will go much deeper into how exactly the Store works.

The Store

The Store is the GlobalState manager. One may subscribe to it — to receive GlobalState change-updates; and dispatch to it — to update the GlobalState.

This 1-directional flow goes like this (this article will cover everything) :

Reactdroid’s Store dispatch flow (uni-directional)

Let’s create our Store. Same as with the Component, we will use an abstract class for platform-specific base-classes to extend (e.g. in AndroidAndroidStore). Extending classes will use Kotlin’s object (a singleton).

Reactdroid’s Store declaration. Simplified.

This constructor consists of 2 arguments:

  1. mainReducer — will hold all the Reducers of the Store (as a List)
  2. preloadedState — the initial state for when the app starts

And the only member is the (global) state object, initialized either with the preloadedState (e.g. some state from the API server), if given, or with the Reducers-defined initial state of the app — provided by the mainReducer (e.g. some state according to the local DB).

The Store’s API

The Store’s core API consists of 2 methods — subscribe() and dispatch().
One to subscribe for GlobalState updates and the other to update it.

Here is a (very) simplified declaration of these 2 API’s :

the Store’s API. Very simplified.

In practice, this declaration is way more complex, with generics and HOCs and whatnot. But we really don’t have time for that now, following articles will go deeper 😉.

As a reminder, Redux (and the Store) have nothing to do with React unless we bind them together (with a Component-dedicated subscribe method).
The Store therefore, is an independent GlobalState manager that anyone can subscribe to by using a pure rx Observer instead of a Component.

The GlobalState

The GlobalState holds the whole state of the whole app.
That means it should be able to hold any type of Object or else we’ll be very restricted. It’s no wonder then, that it’s just a HashMap :)

Reactdroid’s GlobalState.

The map maps a String key to Any? Object (nullable) value.
Also, as you can see, the underlying map is internal — we will use ‘getters’ to easily retrieve values out of it — as this map is going to be huge with lots of hierarchies.

Although this map’s keys are raw String, updating and retrieving from the GlobalState requires StoreKeys.

The GlobalState’s StoreKeys

The StoreKey is basically just a functional interface that returns a String.
That String refers to the GlobalState’s map keys.
The keys in practice will be enums (or maybe Kotlin’s sealed class) which will implement this interface:

the StoreKey for retrieving/dispatching values to the Store. Simplified.

Why not just use Strings? Well, it will become more clear as we go deeper, but for example, this way we can bind a StoreKey to a specific Reducer and add ‘getters’ to it, to easily retrieve its respective value from the Store.

Note: this is where we differ from the JavaScript Redux which requires raw Strings as keys.

The Reducer(s)

This part is little less intuitive. Bare with me 🤓.
The Store holds both the GlobalState and the Reducer(s), and simply put, the Reducer(s) helps updating the GlobalState. That’s it.

Now that we calmed down, let’s also say that each Reducer is in charge of updating its own part of the cake; sorry, the GlobalState.
For example, each Reducer might be in charge of a different feature in our app. Makes sense?

The state machine goes like this:

Store Reducers’ 1-directional flow

On each dispatch, each Reducer receives 3 arguments:
previous-state, (Store)Key and value. Using them, each Reducer produces a new ‘small’ GlobalState — to replace its relative part of the cake; sorry, the ‘big’ GlobalState.
Finally, the Store merges all ‘small’ parts into the ‘big’ GlobalState and notifies its subscribers.

Enough talk, let’s create the Reducer:

Reactdroid’s Reducer. Simplified.

As you can see, the constructor takes a List of ‘child’ Reducers — this is how we create the mainReducer which the Store receives to its constructor.

The onNewAction callback is called by the Store on each dispatch, requesting a new ‘small’ GlobalState part.

Yes, you got that right — both a Reducer’s ‘small’ part of the global state and the Store’s ‘big’, merged global state, have the same type — GlobalState.
As a GlobalState is basically just a HashMap, we therefore have a big HashMap which holds multiple, smaller HashMaps — one per Reducer. Like this:

An example of a ‘raw’ application global state

Last, the getDefaultState() method serves mainly for app starts, when the Store will create its initial GlobalState out of all the Reducers’ default states.
For example, one Reducer might be a DataReducer, which returns all the DB data, which is how we load DB to the Store on app starts :)

To sum this up: the Reducer’s onNewAction is how it controls a specific, smaller part of the whole GlobalState.
Like the Store, it will be extended as a Kotlin object (a singleton).

Summarizing the flow

Before we conclude this article with a connected Component example, let’s have a second look at the flow’s diagram and summarize what’s going on, now that we understand all the parts:

Reactdroid’s Store dispatch flow (uni-directional)
  1. A Component (for example) dispatches an Action(StoreKey, value).
  2. The Store executes onNewAction on all of its Reducers and receives a list of ‘small’ GlobalStates.
  3. The Store merges all ‘small’ parts into 1 ‘big’ GlobalState and updates its
    var state: GlobalState
  4. The Store notifies all subscribers of the new state, e.g. Components.

We’re done 💪. Let’s review a simplified version of everything we did, for perspective:

Reactroid’s Redux implementation overview. Simplified.

This, right there 👆 , is Redux, in Kotlin 🤘 🤓 🤘.

Our first connected Component

Remember our ButtonComponent from the previous article? Its text was controlled by its (Component) parent. Let’s instead connect it to a Store:

a Store-connected ButtonComponent

Our Button’s text is now connected to the Store instead of controlled by its parent. In an actual app, this means you can connect many Components to the same value(s) in a Store, so when it is updated, all of them are re-rendered, automatically, at the same time. No hassle.
Also, the parent in this example, as opposed to the previous one, will not re-render on button clicks, because it updates the Store and not its ownState — also a nice plus.

We will talk about how the connect method works in the next article…

Note: Reactdroid’s mapStateToProps is a merge of 2 of the ‘real’ Redux Connect methods: mapStateToProps + mergeProps.

Summary

We now (almost) know how to create our own Redux implementation, for Kotlin! As you probably understand, there is a lot more to it. Next articles will go deeper, into the connected HOC and the RxKotlin implementation of the Store.
I hope you’re excited like me :)
See you there (coming soon!)

--

--

Guy Michael
Nerd For Tech

Developer and UX consultant; Code Architect; specialized in Android & Web (React.js) applications. An advocate of Kotlin Multiplatform and MVI.