Write your own Redux implementation in Kotlin
In the previous articles, I talked about Redux architecture and the Role of middleware in Redux. There’s no better way to learn something than implementing it yourself. We are going to write a very lightweight implementation of Redux architecture in Kotlin which can be used for our android app.
Quick Recap
Redux is an architecture for predictable state container and state management. It is inspired from Flux and uses some of its components. Let’s go through the important components of Redux in brief.
State
State in Redux describes your app’s state at any given time. State is an immutable object which holds the information about the ‘current state’ of the app.
Action
Action is an object which describes a change in the state. Action may have some payload which is used to update the state.
Reducer
Reducer is a pure function which changes the app state from State A to State B for a given action.
Store
Store is at the center of Redux architecture. A store stores the app state and manages it. Once an action is dispatched, the stores updates the app state with the help of reducers.
Listener
A listener subscribes to the store to receive updates when the app state is updated. In your android app, a typical listener is your Activity, Fragment or View which receives the updated state and renders
a new UI.
Middleware
Middleware are essential in Redux architecture to handle side effects which include logging, IO calls, network calls, etc. When an action is dispatched, the store sends the action to the middleware. The middleware does something (api call, etc) and updates the action and sends it back to the store.
For more details about the components of Redux, I would recommend that you check out the previous articles in the series.
Now that we are familiar with Redux, let’s start writing our own implementation.
Implement Redux Architecture in Kotlin
Kotlin is a feature rich language which gives it significant advantage over Java. Kotlin is now an official language for Android application development so it make sense to learn more about it and start writing our apps in Kotlin.
Let’s begin by defining our components. For convenience and ease of writing the code, we are going to use Kotlin’s higher order functions to represent some of the components.
In Kotlin, Type aliases
provide alternative names for existing types. If the type name is too long you can introduce a different shorter name and use the new one instead. So we are going to use typealias
to define Reducer.
Define components
Breakdown
Action
is just an interface. All our actions will implement the empty interface.Reducer
is a pure function. So we use typealias to define a method definition which takesState
andAction
as input and returnsState
.Subscription
is a typealias for a function which takesState
as input parameter. It does not return anything. This will be ourrender
function.- We defined an interface for
Store
with a genericState
. In Redux, the whole app will have one instace of store and one App state tree object.
Store implementation
Let’s implement the store.
Breakdown
SimpleStore
is an abstract class which takes genericState
. To initialize a new instance, we pass an initial state and a list of reducers.- When a subscription subscribes to the store, we add it to the list of subscriptions and notify it of the current state.
- When an action is dispatched to the store, the store reduces the current state with the help of reducers and notifies all the subscriptions if there’s a change in the state.
Example
Let’s see an example of how our Redux architecture is going to work. For this example, I have chosen to go with an app that searches for movies. It has a SearchFragment
where user can enter a query in the edit text and press a button to begin the search. While, the search is ongoing, the screen will show a spinner and disable the search button. Once the results are available, the screen will hide the spinner and show the list of movies. To keep it simple, we are not going to focus on errors at the moment.
Let’s define a state for the search fragment. The actions associated with it and a reducer.
SearchState
consists ofloading
- whether the app is loading results or not,query
- query that the user entered,movies
- list of movies when the results are loaded.ClearSearch
- action is dispatched when user clicks on the clear button.Search
- action is dispatched when user clicks on the search button. The action hasquery
as the payload.LoadingSearch
- action is dispatched when the api starts loading results. This action will be dispatched by middleware, we’ll implement it later.SearchResultsLoaded
- action is dispatched when the api returns results. It has payload of list of movies.reduceSearchState
is our reducer function associated with this state and the screen. You may have multiple reducers for one state. Typically, we would write a reducer for that state, eg.SearchState
. But here, we have written it forAppState
which is main state tree object and it containsSearchState
.
As you can see, the reducer is not at all tied with the view and it’s so much easier to write unit tests since it does not even depend on anything Android related.
Let’s define our store which we will use in the app. There should be only one instance of the store so we are going to use a singleton.
AppState
is our state tree object which contains the whole state of the app.- We create a new class
AppStore
which works withAppState
. We provide an initial state and a list of reducers. We includereduceSearchState
reducer in the list.
Let’s move on to our View and write a render function.
- We subscribe to the store in
onViewCreated
and unsubscribe inonDestroyView
. render
method takes the state and updates the view.- The view sends appropriate actions when user performs some action.
We just created an implementation of Redux architecture in Kotlin. It’s not complete, but we are getting there. Let’s make it look more like Javascript? I never know if this is a good idea or not.
Make the store more closed
Any piece of code can access Store.instance
and dispatch some action or get the current state to update something else. This breaks the unidirectional flow of Redux. It would become difficult for debugging so it’s better that we restrict the access to the store and its properties.
Dispatch
: We want our store be a bit more restrictive and don’t want it to be accessed from some corner of the app. Let’s define Dispatch. It’s a higher order function passed by the store to its subscribers.Unsubscribe
: Similarly, we do not want some other part of the code to callunsubscribe()
to some other subscriber.- We also restrict the code to access the state. If some component wants to access the state, it can subscribe to the store.
- We updated the definition of
Subscription
. The method now takesDispatch
as a parameter which the store will pass to its subscribers. - When the view wants to unsubscribe from the store, it can just call
Unsubscribe()
orUnsubscribe.invoke()
. - To dispatch an action, the component can now use
Dispatch(action)
orDispatch.invoke(action)
.
Implement the new store
We have updated the definition of store and made it more restrictive. Let’s modify the implementation.
With this new definition of the Redux store, we just need to update somethings in our View and render function. The rest of the code does not change.
Update the View
Store.dispatch(action)
method is no longer accessible, but the store sends Dispatch
function reference when it sends updates regarding the state. So let’s update our render function to have a reference of Dispatch
.
- The fragment keeps a reference to
Dispatch
and uses it to dispatch actions. It chucks away the reference inonDestroyView()
. - The fragment invokes
Unsubscribe
inonDestroyView
lifecycle method to stop getting events from the store.
Summary
We have a working implementation of Redux architecture. If you see the implementation of Store
, it’s very simple and tiny. That is why I like this architecture. It may seem complex at first, but actually it’s really easy once you get your head around it.
If we revisit the flow, the view subscribes to the store for the state updates. The view dispatches actions to the store. The store sends the action and the current state to the reducer. The reducer updates the state if necessary. Once the store receives the updated state, it notifies the view of the update.
We are still missing a crucial component in this implementation - Middleware
. Without middleware, we could not make network calls, log some stuff, or do anything on the background thread. We’ll modify our implementation of the store to include Middleware and write an example of a middleware which makes an api call to get the search results.
Redux architecture series
- Introduction: Redux architecture for android apps
- Middleware: Introduction and implementation
- Write your own Redux implementation in Kotlin
- Add Middleware to your Redux implementation
- Build Battleship Game with Redux - Groundwork
- Implement the Battleship gameplay with Redux
Redux architecture and Android
Learn the Redux architecture and implement the clean architecture it in your Android codebase. This series of posts explores the depth of Redux and guides you through implementing the architecture in Kotlin for your android app.