The article is part of series about Reductor library – Redux implementation for Android.

In Part 0 we made simple TODO application using immutable state and self-written state container. We found that mutable data structures can lead to uncontrolled data changes that could be the root of the unpredictable behaviour. After some code changes, we showed that mutations can be avoided by keeping only one immutable object with mutable reference.

In this article, we will cover how Redux can help with this problem and reimplement our application using Reductor.

Here is our TodoStore (class that manage the state) we end up with last time:

Redux and Reductor

This “Store” pattern looks nice for a particular case. It can help to isolate mutations and make state change testable. But how can we reuse it and decrease the amount of boilerplate code needed every time we want to add and manage any new dataset. And that’s where Redux and Reductor will help us.

Reductor is a minimalistic library that reimplements Redux (Javascript library for state management) in Java. It was created with Android in mind but can be used in any Java application. The main idea is to embrace the power of immutable data structures and functional composition to provide a sophisticated approach to developing rich client-side applications.

Here is how Redux works (from here):

Redux dataflow

The whole idea is really simple:

  • Store object holds and maintains the state
  • To change the state application can dispatch actions to Store
  • For each action Store compute new state using pure function f(s, a) (called reducer)
  • Store replace current state with new state

And that’s it! Though it looks elementary this approach is really powerful. To illustrate this will reimplement our TODO application using this approach with Reductor. But don’t worry, there will be not so many changes from our original example.

So, let’s start!

Installation

First of all, we need to add Reductor as a dependency. Follow Installation for more details.

When added, Store, Reducer and Action classes should be available in the classpath.

Defining the shape state.

Before writing any code, we need to choose how our state should look like. As we only need to store a list of TodoItem, for our simple example that will be List<TodoItem>.

You may wonder: What if we need to store not a single object but a few of them? Can I store 2, 3 or 42 objects with different types in Store? – Yes and No.

Store is designed to store one particular state object of the arbitrary type. However what we can do is to compose several “substates” into one object. We will talk about how to do this in the next article.

Defining actions

As said before, we mutate change our state by dispatching actions. Actions are dispatched by actioin type – string identifier that denotes the action which will be applied to the state.

We can define them just as constants.

However, to handle an action we need a bit more information than just a name. That’s why in Reductor Action is predefined type with two fields:

  • type is String action type we just defined above
  • value is payload you can attach to an action if necessary

Additionally, we can define action creators – helper functions to create actions. This step is not required but is strongly suggested to keep code clean.

Basically, that’s our contract. We defined all possible operations we can perform with a state.

Reducer

As we defined actions, we need to define the way we will react to them. To do this we need to implement Reducer interface.

So the idea is really simple behind this signature. You define a pure function (method) reduce that will apply an action to a state to compute a new state.

Let’s check what we have here:

  • Two pure functions that perform particular actions (copied from our original implementation)
  • Method reduce that takes our state and action. This method does a few things:
    • Dispatch the action by switching on action.type
    • Extract payload per each action if necessary
    • Compute new state and return it
    • Return the previous state if action is not dispatched (action.type is unknown for this Reducer)

We defined our state reducer. As you can see we still keep the principle of using only pure functions to compute a new state. That means that this class is still well testable.

Note: our implementation of reduce method contain some unsafe operations like type casting. Do not worry about this boilerplate code now, as we will get rid of it using annotations later.

Using a Store

After we created our Reducer, we can create our state container – Store.

Creating Store

We do it by calling static method Store.create

Store<List<TodoItem>> todoStore = 
        Store.create(new TodoReducer(), Collections.emptyList());

Store.create takes at least two arguments:

  1. State reducer
  2. Initial state

Store API

Once created, Store API is really simple:

  • To access a state at any moment method getState() is used
  • State can be changed by calling dispatch(action)
  • If necessary, app can register StateChangeListener to be notified when state updates by calling subscribe method

Having this in mind we can rewrite our MainActivity:

As you can see changes are minimal. The structure is the same. The main difference is action dispatching.

Reducing boilerplate

As you can see, to create state container we only need to define two things: Reducer and actions. However, as I mentioned before, there is a bit of ‘untyped’ code in reducer and in action creators. This is because we want to pack different data types into the Action.value. Here is our Reducer again:

But actually, if we decompose the logic of reduce method into sub-methods per each action (as we did before), the logic of reduce becomes really straightforward.

So why do we need to write it every time? Let computer do it for us! And that is why Reductor contains Magic Annotation processor.

First, we need to ensure that reductor-processor dependency is added. Follow Installation for more information.

Then we need to simplify TodoReducer as followed:

What did we do here?:

  • Made class abstract and removed ‘reduce’ method. Reductor processor will generate an implementation of reduce method in child TodoReducerImpl class.
  • Annotated class with @AutoReducer annotation.
  • Annotated each action method with @AutoReducer.Action and provided Action type as annotation value
  • Added static factory method create to instantiate generated class

Nice! Our code is clean and fancy. All the boilerplate is written by a machine. But that’s only the half of the story. What about action creators? Reductor generates them too!

This full generated class out of our Reducer:

Now our actions and Reducer are type-safe, even though all of them are dispatched as Action objects.

Conclusion

In this article, we used Reductor library to implement our TODO example app. The main interaction point is Store object which use user-defined Reducer to change the state.

Reductor library was designed to bring Redux experience into the Java and Android world without sacrificing type safety. That’s why there are such features as auto-generated @AutoReducer.

In next article, we will cover how to compose a state and reducers with Reductor.

Stay tuned. To be continued…