Jetpack组合状态吊装、预览和视图模型最佳实践 [英] Jetpack Compose State Hoisting, Previews, and ViewModels best practices

查看:46
本文介绍了Jetpack组合状态吊装、预览和视图模型最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,Jetpack Compose中推荐的事情似乎是将状态从您的组合中提升出来,使它们成为无状态、可重用和可测试的,并允许在预览中轻松使用它们。 所以不是像

这样的东西
@Composable
fun MyInputField() {
    var text by remember { mutableStateOf("") }
    TextField(value = text, onValueChange = { text = it })
}

您可以像这样提升状态

@Composable
fun MyInputField(text: String, onTextChange: (String) -> Unit) {
    TextField(value = text, onValueChange = onTextChange)
}

这很好,但是一些更复杂的用法怎么办? 让我们假设我有一个由可组合的表示的屏幕,在View和ViewModel之间有多个交互。此屏幕拆分为多个内部可组合内容(例如,一个用于标题,一个用于正文,而正文又被拆分为多个较小的可组合内容)

  • 您不能在Composable中创建ViewModel(至少使用viewModel()可以手动实例化),并在Preview中使用此Composable(预览不支持这样创建ViewModel)
  • 在内部组合中使用ViewModel会使它们成为有状态的,不是吗?

因此,我看到的解决方案是仅在最高可组合级别实例化我的视图模型,然后仅将表示状态的val传递给子可组合对象,并回调ViewModel函数。

但这太疯狂了,我不会通过单独的参数将我所有的ViewModel状态和函数传递给所有需要它们的可组合对象。 例如,将它们分组到data class中可能是一种解决方案

data class UiState(
  val textInput: String,
  val numberPicked: Int,
  ……

并可能为回调创建另一个回调? 但这仍然是在创建一个全新的类,只是为了模拟视图模型已有的东西。

我实际上看不到最好的方法是什么,而且我在任何地方都找不到任何关于这方面的东西

推荐答案

管理复杂状态的好方法是将所需的复杂行为封装到类中,并使用记住函数,同时尽可能多地使用无状态小工具,并在需要时随时更改状态的任何属性。

SearchTextField是一个只使用状态提升的组件,SearchBar有向后箭头和SearchTextField,本身也是一个无状态的可组合组件。这两者与搜索栏的父级之间的通信仅通过回调函数来处理,这使得SearchTextField都是可重用的,并且很容易在预览中使用默认状态进行预览。HomeScreen包含此状态以及您管理更改的位置。

完全实施是posted here

@Composable
fun <R, S> rememberSearchState(
    query: TextFieldValue = TextFieldValue(""),
    focused: Boolean = false,
    searching: Boolean = false,
    suggestions: List<S> = emptyList(),
    searchResults: List<R> = emptyList()
): SearchState<R, S> {
    return remember {
        SearchState(
            query = query,
            focused = focused,
            searching = searching,
            suggestions = suggestions,
            searchResults = searchResults
        )
    }
}

记住函数以保留仅在合成期间评估的状态。

class SearchState<R, S>(
    query: TextFieldValue,
    focused: Boolean,
    searching: Boolean,
    suggestions: List<S>,
    searchResults: List<R>
) {
    var query by mutableStateOf(query)
    var focused by mutableStateOf(focused)
    var searching by mutableStateOf(searching)
    var suggestions by mutableStateOf(suggestions)
    var searchResults by mutableStateOf(searchResults)

    val searchDisplay: SearchDisplay
        get() = when {
            !focused && query.text.isEmpty() -> SearchDisplay.InitialResults
            focused && query.text.isEmpty() -> SearchDisplay.Suggestions
            searchResults.isEmpty() -> SearchDisplay.NoResults
            else -> SearchDisplay.Results
        }
}

并通过将状态传递给其他可组合或按ViewModel作为

来更改UI的任何部分中的状态
fun HomeScreen(
    modifier: Modifier = Modifier,
    viewModel: HomeViewModel,
    navigateToTutorial: (String) -> Unit,
    state: SearchState<TutorialSectionModel, SuggestionModel> = rememberSearchState()
) {


    Column(
        modifier = modifier.fillMaxSize()
    ) {
            
        SearchBar(
            query = state.query,
            onQueryChange = { state.query = it },
            onSearchFocusChange = { state.focused = it },
            onClearQuery = { state.query = TextFieldValue("") },
            onBack = { state.query = TextFieldValue("") },
            searching = state.searching,
            focused = state.focused,
            modifier = modifier
        )

        LaunchedEffect(state.query.text) {
            state.searching = true
            delay(100)
            state.searchResults = viewModel.getTutorials(state.query.text)
            state.searching = false
        }

        when (state.searchDisplay) {
            SearchDisplay.InitialResults -> {

            }
            SearchDisplay.NoResults -> {

            }

            SearchDisplay.Suggestions -> {

            }

            SearchDisplay.Results -> {
 
            }
        }
    }
}

这篇关于Jetpack组合状态吊装、预览和视图模型最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆