Thinking in React for Native Android Apps
Tags: react, android, kotlin, reactnative
Published: 2019-09-20 13:50:50

---
title: Thinking in React for Native Android Apps
published: true
description: A guide using MvRx, Navigation, Flexbox and some Kotlin sugar.
tags: react,android,kotlin,react-native
---


_This article was originally published on my personal [blog](https://osamaq.com/thinking-in-react-for-writing-native-android-apps/)._

---

I like writing apps using React Native. In contrast, working on Java Android apps has always been less satisfying because:

1. Boilerplate.
2. ConstraintLayout? LinearLayout? RelativeLayout? TableLayout? GridLayout?
3. [???](https://i.stack.imgur.com/fRxIQ.png)

It's a simple fact that the more code you write, the more likely you are to write bugs. I knew using Kotlin would improve this aspect, but I wanted to get more experienced with Java, so that I'd better appreciate Kotlin when I made the move. I also didn't believe that just by using Kotlin, I'd suddenly really enjoy developing native apps.

Recently, I came across [MvRx (Mavericks)](https://github.com/airbnb/MvRx/wiki). An Android framework [open-sourced](https://medium.com/airbnb-engineering/introducing-mvrx-android-on-autopilot-552bca86bd0a) by Airbnb. I learned that it is conceptually [inspired by React](https://github.com/airbnb/MvRx/wiki#mvrx-for-react-engineers) which piqued my interest. It even brings over the familiar `setState()` syntax. Since MvRx is Kotlin-only, it tipped me over to start learning Kotlin.

> The same todo app written using MvRx is about [1000 lines of code _less_ than the same app written in Java](https://github.com/airbnb/MvRx/tree/master/todomvrx).

Syntax-wise [Kotlin has many similarties to TypeScript](https://gi-no.github.io/kotlin-is-like-typescript/) which I've always preferred using. I learned by going through the Kotlin official docs (which are awesome) and doing some of the the [Kotlin Koans](https://play.kotlinlang.org/koans/overview).

They say that no Java developer has tried Kotlin and wanted to go back to writing Java. I concur.

I previously used [MVP architecture](https://github.com/osamaq/android-mvp-tutorial), whereas MvRx is MVVM. Presenters and ViewModels harbour the logic of your screens (similar to container components in React). The main difference between them is that a ViewModel never holds a reference to any view. It simply updates its state and the view observes the data changes and much like React, re-renders accordingly. Therefore, there is no fear of referencing a `null` view (similar to calling `setState()` on an unmounted React component). This greatly simplifies dealing with view lifecycles.

![Lifecycle vs ViewModel](https://i.imgur.com/xiAMXpf.png)

During my process of learning MvRx and Kotlin, I came across a few helper libraries that improve development experience. I decided to learn and use them.

I slowly came to the realization that sometimes, we might not enjoy working with a framework not because of the framework itself, but simply because of the way we apply it.

- - -

## Guide

We're going to make a simple app utilizing the following libraries:

* Navigation Component
* Kotlin Android Extensions
* MvRx
* Flexbox Layout

The app flow will be as such:

1. Login Screen:
   * Two text input fields.
   * Login button.

Once the user presses the login button, we will mock a request using a simple delay. During the mock request, we will hide the view and show a loading indicator. Once the request is done, we will restore our view, hide the loading indicator and navigate to the landing screen.

2. Landing Screen:
   * This screen will simply show the data entered in the previous text inputs and a logout button.

Simple enough.

### Dependencies

Let's start by creating a blank Android Studio project with Kotlin and add our dependencies.

![New android studio project](https://i.imgur.com/bj81gfn.png)

Add MvRx and the navigation component to your dependencies block, under `app/build.gradle`:

```
dependencies {
    def navVersion = "2.1.0"
    def mvrxVersion = '1.1.0'

    // Navigation component
    implementation "androidx.navigation:navigation-fragment-ktx:$navVersion"
    implementation "androidx.navigation:navigation-ui-ktx:$navVersion"

    // MvRx
    implementation "com.airbnb.android:mvrx:$mvrxVersion"

    // Flexbox
    implementation 'com.google.android:flexbox:1.1.0'
    ..
```

At the top of the same file:

```
// Kotlin Android Extensions
apply plugin: 'kotlin-android-extensions'
```

### Layouts

We will use **single activity - multiple fragments** pattern. Fragments should be designed as resuable and modular components, just like presentational components in React. 

Create our layout files: Right-click the `res` folder then select **New > Android Resource File**. Set the type as `Layout`.

![Layout files](https://i.imgur.com/Xdkq8hp.png)

`activity_main.xml`

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <fragment
        android:id="@+id/host"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
```

`login_fragment.xml`

```
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F1F1F1">

    <com.google.android.flexbox.FlexboxLayout

        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:layout_marginTop="200dp"
        app:alignItems="center"
        app:flexWrap="wrap"
        app:justifyContent="center">

        <EditText
            android:id="@+id/loginNameText"
            android:layout_width="120dp"
            android:layout_height="60dp"
            android:hint="Name"
            android:importantForAutofill="no"
            android:inputType="text"
            app:layout_flexBasisPercent="80%"
            tools:text="Name" />

        <EditText
            android:id="@+id/loginCityText"
            android:layout_width="120dp"
            android:layout_height="60dp"
            android:hint="City"
            android:importantForAutofill="no"
            android:inputType="text"
            app:layout_flexBasisPercent="80%"
            tools:text="City" />

        <Button
            android:id="@+id/loginButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:backgroundTint="#6200EE"
            android:text="LOGIN"
            android:textColor="#FFF"
            app:layout_flexBasisPercent="80%" />

        <ProgressBar
            android:id="@+id/loadingIndicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"
            app:layout_flexBasisPercent="100%"
            />

    </com.google.android.flexbox.FlexboxLayout>


</FrameLayout>
```

The root is a `<FrameLayout/>`. The `<fragment/>` tag in `activity_main.xml` will be swapped for the contents (children) of `<FrameLayout/>` in each of our fragments. (a bit like React's [children](https://stackoverflow.com/questions/49706823/what-is-this-props-children-and-when-you-should-use-it)?)

The child of the root layout is [`<FlexboxLayout/>`](https://github.com/google/flexbox-layout). Pretty cool. [`ConstraintLayout`](https://developer.android.com/training/constraint-layout) is nice if you prefer to customize the layout visually. 

Feel free to use whatever you fancy.

![Login fragment layout](https://i.imgur.com/EoOCcJI.png)

`landing_fragment.xml`

```xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F1F1F1">

    <com.google.android.flexbox.FlexboxLayout
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:layout_marginTop="200dp"
        app:alignItems="center"
        app:flexWrap="wrap"
        app:justifyContent="center">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="@android:color/black"
            android:textSize="24sp"
            app:layout_flexBasisPercent="50%"
            android:text="Name:" />

        <TextView
            android:id="@+id/landingNameText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="@android:color/black"
            android:textSize="24sp"
            app:layout_flexBasisPercent="50%"
            tools:text="placeholder" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="@android:color/black"
            android:textSize="24sp"
            app:layout_flexBasisPercent="50%"
            android:text="City:" />

        <TextView
            android:id="@+id/landingCityText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="@android:color/black"
            android:textSize="24sp"
            app:layout_flexBasisPercent="50%"
            tools:text="placeholder" />


        <Button
            android:id="@+id/logoutButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:backgroundTint="#F05E54"
            android:text="LOGOUT"
            android:textColor="#FFF"
            app:layout_flexBasisPercent="80%" />

    </com.google.android.flexbox.FlexboxLayout>


</FrameLayout>
```

![Landing fragment layout](https://i.imgur.com/iTUjrWC.png)

### Classes

Create our Kotlin classes to associate with each layout.

![Classes](https://i.imgur.com/4dq3iEl.png)

To create an activity, we would usually extend the `AppCompatActivity` class directly. But since we want to use MvRx, we will extend `BaseMvRxActivity` instead (which inherits from `AppCompatActivity`) for MvRx support. We will also override `onCreate()` and inflate `activity_main.xml` here.

> Tip: [here](https://developers.google.com/android/for-all/vocab-words/?hl=en) is a super useful page for looking up Android lingo like 'inflate'.

`MainActivity.kt`

```kotlin
package com.example.mymvrxapp

import android.os.Bundle
import com.airbnb.mvrx.BaseMvRxActivity


class MainActivity() : BaseMvRxActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
```

Every activity must be registered in the manifest. We will register `MainActivity` and set it as the starting activity.

`AndroidManifest.xml`

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mymvrxapp">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name="com.example.mymvrxapp.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
```

For our fragments, we will extend `BaseMvRxFragment` instead of `Fragment`. We must also implement `invalidate()`. We will leave it empty for and go over it later on.

`LoginFragment.kt`

```kotlin
package com.example.mymvrxapp

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.BaseMvRxFragment

class LoginFragment : BaseMvRxFragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.login_fragment, container, false)
    }

    override fun invalidate() {
    }
}
```

`LandingFragment.kt`

```kotlin
package com.example.mymvrxapp

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.BaseMvRxFragment

class LandingFragment : BaseMvRxFragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.landing_fragment, container, false);
    }

    override fun invalidate() {
    }
}
```

### Navigation

If we run the app at this point, its going to crash. Our `<fragment/>` in `activity_main.xml` needs an ID and a name to associate it with a fragment. We've given it an ID but we haven't told it what fragment its going to host yet.

![Fragment name prop](https://i.imgur.com/bwalFSz.png)

We're not going to do this. Instead, we are going to associate it with our navigation graph. using the [Navigation Component](https://developer.android.com/guide/navigation/). 

Simply put, it's a library that simplifies how we handle navigation with a neat API and a friendly interface to visualize our routes.

> The navigation component fulfills the same job as [React Navigation](https://reactnavigation.org/) and [React Router](https://reacttraining.com/react-router/).
>
> Fun React Navigation Fact: We can [configure]((https://reactnavigation.org/docs/en/react-native-screens.html)) it to use FragmentActivity
> instead of plain `View`'s under the hood for performance improvements.

Create our navigation graph. Right-click the `res` folder and then select **New > Android Resource File**. Set the type as `Navigation`.

`nav_graph.xml`

```xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/nav_graph" />
```

Now that we've created the file for our navigation graph, we will add an ID to `<fragment/>` and designate it as our navigation host by adding the following attributes:

`activity_main.xml`

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <fragment
        android:id="@+id/host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

</LinearLayout>
```

Lets add our fragment classes to the navigation graph to mark them as possible destinations. I like to use the visual editor for this part. 

Open `nav_graph.xml` in the visual editor, and add `LoginFragment` and `LandingFragment`.

![Adding destinations](https://i.imgur.com/JHOwaX8.png)

Select the login fragment in the graph then click the home icon to mark it as the starting destination. 

Next, drag from the edge of the login fragment to the landing fragment to create a navigation action. 

![Route](https://i.imgur.com/a80Zbq5.png)

Now your navigation graph and markup should look similar to this. 

`nav_graph.xml`

```xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/loginFragment">
    <fragment
        android:id="@+id/landingFragment"
        android:name="com.example.mymvrxapp.LandingFragment"
        android:label="LandingFragment"
        tools:layout="@layout/landing_fragment" />
    <fragment
        android:id="@+id/loginFragment"
        android:name="com.example.mymvrxapp.LoginFragment"
        android:label="LoginFragment"
        tools:layout="@layout/login_fragment">
        <action
            android:id="@+id/action_loginFragment_to_landingFragment2"
            app:destination="@id/landingFragment" />
    </fragment>
</navigation>
```

I've added `tools:layout=...` attribute for displaying your layouts in the navigation graph, otherwise you'd only see a plain and boring rectangle.

![Nav graph complete](https://i.imgur.com/nAWcI9o.png)

If we run the app now, we should see the Login screen.

### Logic

Lets start by adding state to our text inputs. We need to do the following:

* Create our data class which describes the shape of our state.
* Create our view model class which will hold functions that trigger our state changes.

I created both the data class and the view model in the same file as `MainActivity.kt` for convenience but that is not a requirement.

`FormState`

```kotlin
data class FormState(
    val name: String = "",
    val city: String = "",
    val loggedIn: Async<Boolean> = Uninitialized
) : MvRxState
```

We must set the initial state by providing default arguments. Notice it implements `MvRxState`. That's required for any data class we wish to use as state.

In React, we might have a `loading` state and set it before and after the completion of asynchronous tasks. In MvRx, [`Async`](https://github.com/airbnb/MvRx/wiki#async) is a [sealed class](https://kotlinlang.org/docs/reference/sealed-classes.html) that comes with types like `Loading` and `Success`. We can simply refer to the current type of the async value to react to loading and success states. Super helpful.

`FormViewModel`

```kotlin
class FormViewModel(initialState: FormState) :
    BaseMvRxViewModel<FormState>(initialState, debugMode = BuildConfig.DEBUG) {

    init {
        logStateChanges()
    }

    fun setNameAndCity(name: String, city: String) {
        setState { copy(city = city, name = name) }
    }
    // We will go over this one in depth later on
    fun doLogIn() {
        Single.just(true)
            .delaySubscription(5, TimeUnit.SECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .execute { copy(loggedIn = it) }
    }

    fun doLogout() {
        setState {
            copy(loggedIn = Uninitialized)
        }
    }
}
```

`debugMode = BuildConfig.DEBUG` will make some [safety checks](https://github.com/airbnb/MvRx/wiki#debug-checks) when working with the debug build. The `init` block and `logStateChanges()` are also optional. `logStateChanges()` does exactly what it says. We will show its output when we finish our app.

Our `setState` reducers will be called from our views to update the state. Similar to React, the `setState` block is an async operation and a pure function that takes in the current state, and returns the new state.

Notice the `copy()` syntax within the `setState` block. Inside `setState`, `this` keyword would be our data class and `copy()` is a [method](https://kotlinlang.org/docs/reference/data-classes.html#copying) that belongs to data classes in Kotlin. It allows you to modify select properties instead of all (we don't need to spread the current state, in React lingo).

Next, we want to be able to access state from our fragments. Our login and landing fragments must subscribe to the same view model _instance_ we defined in our main activity.

`LoginFragment.kt`

```kotlin
class LoginFragment : BaseMvRxFragment() {
    // Fetch the ViewModel scoped to the current activity or create one if it doesn't exist
    private val formViewModel: FormViewModel by activityViewModel()
    
    ..
```

`LandingFragment.kt`

```kotlin
class LandingFragment : BaseMvRxFragment() {    
    // Fetch the existing ViewModel scoped to the current activity
    private val formViewModel: FormViewModel by existingViewModel()
    
    ..
```

Notice `by activityViewModel()`. It is a Kotlin [delegate](https://kotlinlang.org/docs/reference/delegated-properties.html) that lazily returns a view model scoped to the current activity. Since both of our fragments belong to the same activity, sharing state is very straightforward.

For `LandingFragment.kt`, we used `existingViewModel()` which returns the existing view model in scope of the current activity. The difference is that this function will throw an exception if no view model exists, instead of creating a new one.

> MvRx lets you [easily choose the scope](https://github.com/airbnb/MvRx/wiki#creating-and-subscribing-to-a-viewmodel) of the view model you'd like to work with.

- - -

As soon as our view loads (React: mounts), we're going to:

* Add a click listener to our login button.
* When the user presses the button we will grab the user's input and update our `name` and `city` states, then call `doLogIn` to start the mock request/delay.
* When the delay starts, we must hide our view and show the loading indicator.
* When the delay ends, we must hide the loading indicator and show our view. Then, we navigate to the landing screen.

Override `onViewCreated` and implement the on-click listener as described:

`LoginFragment.kt`

```kotlin
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        loginButton.setOnClickListener {

            // Update the state
            formViewModel.setNameAndCity(
                loginNameText.text.toString(),
                loginCityText.text.toString()
            )

            formViewModel.doLogIn()
        }
    }
```

Because of [Kotlin Android Extensions](https://kotlinlang.org/docs/tutorials/android-plugin.html), we are able to directly reference the view without calling `findViewById`. This is called View Binding (similar to getting a `ref` to a node in React) .

> Note: in the future, it will be recommended to use the [View Binding component](https://developer.android.com/topic/libraries/view-binding) which has some advantages and none of the [drawbacks](https://old.reddit.com/r/androiddev/comments/ala9p2/why_kotlinx_synthetic_is_no_longer_a_recommended/efdvpkg/). It will be available in newer versions of Android Studio.

```kotlin
    fun doLogIn() {
        Single.just(true)
            .delaySubscription(5, TimeUnit.SECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .execute { copy(loggedIn = it) }
    }
```

> MvRx makes use of RxJava. Primer: RxJava is very useful in Android development as it offers a neat API for handling asynchronous logic (Like JavaScript promises, but with more capabilities). 
>
> It is important to execute long running tasks off the main thread (also called UI thread) to prevent the app from dropping frames. RxJava allows you to declaratively switch threads with ease. 
>
> To use any of RxJava's APIs, we must work with the `observable` type. RxJava has APIs to create observables from things.

`doLogin()` is called when the login button is pressed. Let's go through it in detail as Rx can be intimidating if you've never used it before:

* `Single` is a type of observable that resolves to a single value, exactly like a JavaScript promise.
* `just()` is used to denote that this observable resolves to just this item, in this case the item is `true`. So the first line is equivalent to `Promise.resolve(true)` in JavaScript.

```kotlin
..
.delaySubscription(5, TimeUnit.SECONDS)
```

* We need to subscribe to an observable to receive a result from it. This line states that any subscription should be delayed by 5 seconds.

```kotlin
..
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
```

* Here, we state that we wish to subscribe to this observable using an `io` thread by the help of the `io` scheduler and ask for value notifications to be sent to the main thread.

```kotlin
..
.execute { copy(loggedIn = it) }
```

* `execute` is a neat helper function by MvRx that maps the current state of this process to our `loggedIn` async type. When the observable is executed, `loggedIn` type is updated to `Loading`. When it is done, `loggedIn` type and value are updated to `Success` and `true`.

Now, the `invalidate()` function comes in handy. This function is called every time our state is updated (just like a React re-render). Here, we can make changes to our view according to the current state.

`LoginFragment.kt`

```kotlin
    ..

    override fun invalidate() {
        withState(formViewModel) { state ->
            loadingIndicator.isVisible = state.loggedIn is Loading
            loginNameText.isVisible = state.loggedIn !is Loading
            loginCityText.isVisible = state.loggedIn !is Loading
            loginButton.isVisible = state.loggedIn !is Loading

            if (state.loggedIn is Success) {
                findNavController().navigate(R.id.action_loginFragment_to_landingFragment2)
            }
        }
    }
```

`withState` allows us to access the current state of our view model. Inside, we map the loading state of `loggedIn` to the visibility of our loading indicator, inputs and button. If `loggedIn` is of type `Success`, then we navigate to the landing screen. 

> [Navigation KTX](https://developer.android.com/kotlin/ktx#navigation) helps us obtain a reference to the navigation controller here.

For the landing fragment, we need to implement `invalidate()` and update our texts using the current state. We will add a listener to our logout button that sets `loggedIn` to `Uninitialized` and then pops our fragment off of the navigation stack, going back to the login screen.

`LandingFragment.kt`

```kotlin
    ..

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        logoutButton.setOnClickListener {
            formViewModel.doLogout()
            findNavController().popBackStack()

        }
    }

    override fun invalidate() {
        withState(formViewModel) { state ->
            landingCityText.text = state.city
            landingNameText.text = state.name
        }
    }
```

![Mymvrxapp gif](https://i.imgur.com/Nuk7agr.gif)

Courtesy of `logStateChanges()`:

```
D/FormViewModel: New State: FormState(name=Osama, city=Cyberjaya, loggedIn=com.airbnb.mvrx.Uninitialized@24591c4)
D/FormViewModel: New State: FormState(name=Osama, city=Cyberjaya, loggedIn=com.airbnb.mvrx.Loading@7749791c)
D/FormViewModel: New State: FormState(name=Osama, city=Cyberjaya, loggedIn=Success(value=true))
```

All done! Hope you've enjoyed this guide and found it useful.

If you'd like to learn more about MvRx, I suggest going through their [wiki](https://github.com/airbnb/MvRx/wiki) and code samples on their [repo](https://github.com/airbnb/MvRx/).