Diffing state in Mavericks(MvRx)

Photo by Possessed Photography on Unsplash

One of the things that frequently comes in my mind when thinking about state management is diffing. As someone who is familiar with React JS I was impressed by the diffing done by JS using virtual DOM. Well, this will all change with Jetpack Compose. But for the time being, let’s try to solve this problem.

The Problem

Let’s assume you have this state class.

data class HomeState(
val movies: List<Movies> = listOf(),
val pageTitle: String = "") : MavericksState

With normal approach like below

override fun invalidate() = withState(viewModel) { state ->   // render the list
// render the title

the list will render every time you change the title and depending on the complexity of the screen these type of small changes (like title) might result in expensive layout in your app.

I will be using this app as reference. The app is pretty simple, it allows you to search movies using omdb API. You can find the code in their respective branches.

The solution

Using onEach function

We can use the onEach function to do the diffing. You can find the code here.

We can get the diffing using onEach in the onCreate function of the fragment.

Above approach uses reflection. This might result in delay when you first initialize the screen as it needs to warmup the cache as mentioned here but I didn’t find any performance difference. I wasn’t sure if onEach can be used like this but from the github issue, this approach is fine.

Using diffCopy in state

This is just a plain old way of diffing. The idea is simple, we use both the old and current state and do the diffing. You need to modify your state like this. You can find the code here.

We define diffCopy function that should be used instead of the copy function provided by the data class. We define oldState that stores the previous state.

And to do the diffing we just expose nullable computed property that only returns the value if it has changed otherwise returns null.

Then we need to use the computed property instead of the actual state property in the view.

In your viewModel the only thing that you need to do is to use diffCopy instead of copy. If you need to force render the state, like when the view is popped from the backstack, you can just pass null as oldState like this.

As you can see when isRestored is true (the view is restored from backstack) we can set the oldState to null to force render the UI.

As we can see the second approach is simple but requires more boiler plate and is difficult to maintain. On my basic testing I didn’t find much performance difference between the two approach. So I think the first approach is the way to go if you want diffing in you screen.

android developer