-
Notifications
You must be signed in to change notification settings - Fork 500
Advanced Concepts
MvRx supports adding dependencies via constructor injection. To leverage this, create a companion object
in your ViewModel and have it implement MvRxViewModelFactory<YourState>
. That will force you to implement a create
function. It will pass in the FragmentActivity
that hosts the fragment. You should use that, if needed, to get or create your dagger/DI component and inject what you need. DO NOT pass a reference of your activity to your ViewModel. It will leak your Activity. Pass the application context if you must.
There may be times in which you want to subscribe to state changes outside of it simply calling invalidate()
on your view. From within your viewModel, you can simply call subscribe { ... }
From outside, you must pass in a LifecycleOwner
. The Airbnb MvRxFragment adds this extension which makes subscribe automatically pass itself in.
If you need the previous version of the state in addition to the new state, you can use subscribeWithHistory
instead of subscribe
.
MvViewModel
subscriptions have an optional shouldUpdate
parameter. It is a lambda that takes the old state and new state and returns whether to call the subscriber. This is functionally equivalent to having an if
block in the beginning of your subscriber.
MvRx comes with a few shouldUpdate
helpers out of the box including:
-
onSuccess
: Calls the subscriber when anAsync
state property transitions from non-Success
toSuccess
. -
propertyWhitelist
: A vararg of state properties to monitor for updates.
Call logStateChanges()
in the init
of your ViewModel and if debugMode
is set to true, it will log all state changes to logcat. Assuming your state class is a Kotlin data class, the toString() should be readable.
A successful pattern for pagination has been to have one state property store Async<List<T>>
while another stores List<T>
. The Async
property contains the network request of the current page. This can be checked to prevent us from requesting duplicate pages simultaneously and can be queried to determine whether or not to show a loading indicator.
The reducer for the pagination request should append results to the results list if it is successful like:
MvRxReviewsRequest.create(1234, offset).execute {
copy(reviews = reviews.appendAt(it()?.reviews, offset), reviewRequest = it)
}
MvRx also includes appendAt
to replace everything after the specified offset with the results. It handles edge cases like ignoring appending null and handling index out of bounds issues.
If you have an async request that is dependent on another, execute them like this:
/* In ViewModel */
init {
onSubscribe(onSuccess(MyState::request1)) {
fetchRequest2()
}
}
You can log all state changes to logcat with logStateChanges
MvRx has a debug mode that enables some new checks. When you integrate MvRx into your app, you will need to create your own MvRxViewModel
that extends BaseMvRxViewModel
. One of the required things to override is protected abstract val debugMode: Boolean
. You could set it to something like BuildConfig.DEBUG
.
When debugMode
is enabled, MvRx:
- Will run your
setState
reducers twice and assert that they are equal. This is to enforce that reducers are pure functions. - Will run several immutability asserts on your State class.
- Require your state class to be public so that it can be created by MvRx.