https://kotlinlang.org logo
#compose
Title
# compose
l

lesincs

07/28/2021, 1:28 PM
What’s the best practice to manage state in JetPack Compose? See thread。
👍 1
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:
Copy code
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:
Copy code
@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。
Copy code
_state.value = state.value.copy(userName = userName)
So I want to ask for if there are some good way to manage state?
z

Zach Klippenstein (he/him) [MOD]

07/28/2021, 3:54 PM
That looks like state hoisting to me - you’ve hoisted your composable’s state into your view model
☝️ 1
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
l

lesincs

07/29/2021, 12:41 AM
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.
z

Zach Klippenstein (he/him) [MOD]

07/29/2021, 3:39 PM
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:
Copy code
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.
10 Views