3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

rememberViewModelStoreProviderを使ったScoped ViewModel

3
Posted at

Jetpack Lifecycle 2.11.0 で rememberViewModelStoreProvider が追加され、特定の Composable fun のスコープに ViewModel を紐づけることができるようになりました。

これを書いているのは Lifecycle 2.11.0-beta01 時点の内容なので、使用する際はライブラリのバージョンと最新情報を併せて確認してください。

実装

rememberViewModelStoreProviderrememberViewModelStoreOwner を使用して、そのスコープ内だけの ViewModelStoreOwner を作ります。rememberViewModelStoreProvider の呼び出しスコープで ViewModel の生存範囲が決まります。
LocalViewModelStoreOwner に渡すことで、スコープ内での ViewModel の生存期間が rememberViewModelStoreProvider の呼び出したスコープになります。

val storeProvider = rememberViewModelStoreProvider()
val owner = rememberViewModelStoreOwner(provider = storeProvider)
CompositionLocalProvider(LocalViewModelStoreOwner provides owner) {
    val viewModel = viewModel { HogeViewModel() }
}

ユースケース

Pager

HorizontalPager/VerticalPager と組み合わせることで、Composition 中の Pager のコンテンツのスコープで ViewModel を扱うことができます。
rememberViewModelStoreProvider の呼び出しのスコープで ViewModel の生存期間が決まるため、Pager の外側に置くことで Pager のスコープの範囲で ViewModel のインスタンスが保持されます。viewModel(key = XXX) でインスタンスを Pager のコンテンツごとに分けていたこれまでのよくある実装と同様の処理になります。

@Composable
fun PagerSample() {
    val storeProvider = rememberViewModelStoreProvider()
    val pages = listOf(...)

    HorizontalPager(pageCount = pages.size) { page ->
        val pageOwner = rememberViewModelStoreOwner(provider = storeProvider, key = page)

        CompositionLocalProvider(LocalViewModelStoreOwner provides pageOwner) {
            val pageViewModel = viewModel { HogeViewModel(pages[page]) }
        }
    }
}

Dialog

Dialog 系と組み合わせることで、Dialog が表示しているスコープで ViewModel を扱うことができます。
Scoped ViewModel を使わない実装の場合、Dialog を再度表示した時に以前表示した ViewModel のインスタンスがそのままなので ViewModel の状態が使いまわされてしまう、ということがありましたが、Scoped ViewModel を使うことで表示ごとに異なるインスタンスが使われるので回避することができます。

@Composable
fun PagerSample() {
    var openBottomSheet by rememberSaveable { mutableStateOf(false) }

    if (openBottomSheet) {
        val scopedOwner = rememberViewModelStoreOwner()
        CompositionLocalProvider(LocalViewModelStoreOwner provides scopedOwner) {
            val viewModel = viewModel { HogeViewModel() }
            ModalBottomSheet(
                ...
            ) {
                ...
            }
        }
    }
}

リンク

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?