Thread
#compose
    lesincs

    lesincs

    1 year ago
    What’s the best practice to manage state in JetPack Compose? See thread。
    In our current project, we manage our state in this way: • One screen to one state. • In any compose hierarchy,we don’t do state hoisting,we just collect the whole
    state
    and call the ViewModel’s function directly. For example,if we have a login screen。Our
    ViewModel
    may look like this:
    class LoginViewModel : ViewModel() {
      private val _state = MutableStateFlow(LoginScreenState())
      val state: StateFlow<LoginScreenState>
        get() = _state.asStateFlow()
    ''  fun onUserNameChange(userName: String) {
        _state.value = state.value.copy(userName = userName)
     }
     
     // ......... ignore other function ..........
    '}
    'data class LoginScreenState(
      val isLogin: Boolean = false,
      val userName: String = "",
      val password: String = ""
    )
    The screen is like this:
    @Composable
    fun LoginScreen() {
      //...
      
      UserName()
      PassWord()
      
      //......
    }
    
    @Composable
    fun UserName(){
      val viewModel = viewModel<LoginViewModel>()
      val state by viewModel.state.collectAsState()
      
      TextField(value = state.userName,onValueChange = {
        viewModel.onUserNameChange(it)
     }) 
    }
    Some one may wonder why we don’t do
    state hoisting
    , because in the official pathway, they do
    state hoisting
    , and in some official demos, they do
    state hoisting
    as well. But we use
    compose
    in a commercial project instead of a demo, so in our project, our
    compose
    hierarchy is much deeper than the demo. So there may be so many properties or action we need to
    state hoisting
    , it’s a pretty painful process. So we just write code in below way, the benefit is we can get the sate of the screen easily, and we can just call the function of
    ViewModel
    directly. But there is a concern in my mind that is even only the password’s state change will toggle the
    UserName
    composable recomposition, so I’m not sure if there is a performance problem in our way. Also I think in our way updating a state is painful process as well. We need to write this boilerplate everywhere。
    _state.value = state.value.copy(userName = userName)
    So I want to ask for if there are some good way to manage state?
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    That looks like state hoisting to me - you’ve hoisted your composable’s state into your view model
    Changing the password state shouldn’t trigger a recomposition of the username, they’re in separate recompose scopes and observe independent state.
    You have a simple reducer pattern there. There are many libraries that try to help you implement architecture patterns like that, eg Mavericks, Oolong, etc. None of that is really compose-specific though
    lesincs

    lesincs

    1 year ago
    Thanks, but I am still confused about some questions:1. If that’s is
    state hoisting
    , so my question is do I need to make any none-top
    Composable
    stateless? 2. Secondary, you said “Changing the password state shouldn’t trigger a recomposition of the username ..“, but both of them observes just one same state, right? Is there some optimization
    Compose
    did under the hood? 3. For the reducer libraries, I will take a look at them, thanks.
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    You’re right, they’re observing the same state (I didn’t read closely enough, just saw “username” in your sample viewmodel). You could fix that trivially by doing this in your ViewModel:
    val userName = state.map { it.userName }
    Note that this is an assignment, not a
    get()
    override – that’s important because the assignment creates a single new Flow which will be used by every recomposition, whereas doing the map in a
    get()
    would create a new flow every call, which would technically work in this case but would be wasteful and could cause issues in the future.
    If that’s is 
    state hoisting
    , so my question is do I need to make any none-top 
    Composable
     stateless?
    I’m not sure why you’re asking. State hoisting is just the name for a pattern that comes up a lot with declarative frameworks. There are different ways to hoist state. Whether your composables should be stateless or not depends on what kind of API it makes sense for them to provide. If you want them to be stateless, you might find state hoisting a useful tool.