I've seen the pattern of passing `ViewModel` as a ...
# compose
m
I've seen the pattern of passing
ViewModel
as a default argument in
Composable
a bunch of times. Last example in NIA:
Copy code
internal fun BookmarksRoute(
    onTopicClick: (String) -> Unit,
    modifier: Modifier = Modifier,
    // No one ever passes a different value here
    viewModel: BookmarksViewModel = hiltViewModel(),
) {
Is there a specific reason for this? I've see this message but it's 2 years old and it mentions DI but I don't really see myself faking the ViewModel in tests (it's a final class). Or should I? Is there still a reason to use this pattern?
e
Dont “use” it, at least dont make it your primary composable function. You should only ever use that function in “DI” overloads, and the real composable that you test and preview etc should have plain state parameters.
Copy code
// This composable is just DI glue
@Composable fun DiscoverUi(vm: VM = hiltVModel) {
  DiscoverUi(vm.state)
}

// This is the composable you test & preview
@Composable fun DiscoverUi(state: DiscoverState, modifier: M = Modifier) {
  Box|Column|Scaffold etc
}
m
Thanks! But then what's the point of making it a parameter? i.e. why
Copy code
@Composable fun DiscoverUi(vm: VM = hiltVModel) {
  DiscoverUi(vm.state)
}
and not
Copy code
@Composable fun DiscoverUi() {
  val vm: VM = hiltVModel(...)
  DiscoverUi(vm.state)
}
?
What's the use case for calling
DiscoverUi()
with a different viewModel?
e
Oh that one I dont even know. I do the second one you posted personally (for my presenters, I dont use hilt).
m
Gotcha 👍
I'll submit a PR to NIA see if that drives a discussion
c
I use Koin and not Hilt. In my case the code to obtain the ViewModel is separated from the composable that uses said ViewModel. For example I might be using injection-with-para meters to the ViewModel (often happens when using nav args). I don't want the Composable to know about all these details. I actually don't use the default value for the VM parameter in my Composables.
a
If you want to create a view model using some fake dependencies (e.g. fake repositories) for testing, how would you do that with the second pattern? Also see https://twitter.github.io/compose-rules/rules/#viewmodels.
k
@Albert Chang I don’t think anything precludes from that. The viewModel can still have constructor injection, right?
a
Sure, but that's implicit. It's the same reason as why composition locals should be avoided.
k
@Albert Chang if we want to avoid calling for the viewModel within the composable and instead prefer passing it in, then I really hope to find an answer to this SO question I asked yesterday. Here’s hoping someone has some ideas (I first brought it up in #dagger here, as it really feels like the Hilt piece is what’s going wrong, but only during tests for some reason).
a
Seems that you haven’t understood the reason to use this pattern. Stop using DI frameworks and just inject the dependencies manually. If you don’t want to do that in the app code, at least do that in the tests. That will make you code much easier to read to maintain.