Handling State | Jetpack Compose
In Android Jetpack Compose Paradigm, State in composables are of very importance, without it we cannot change the behavior of different components and cannot update any data at runtime.
All apps show some state to its user, we can understand it by a simple use case or a user interaction. Whenever there is an interaction with app directly or indirectly in response we have a state change.When state variable changes it triggers a particular UI Element that interacts with it. In imperative XML based views, there is an implicit change or recomposition of states but in Compose there is an explicit way and to acheive that we have to tell the composable about the new state in order to acheive the state change.
@Composable
fun LocalStateChange() {
Column() {
Text(text = "Enter")
OutlinedTextField(
value = "",
onValueChange = { },
label = { Text(text = "Name") })
}
}
Nothing happens here beacause we did not call the composable with value parameter for a state change.
To do this, we can use Remember Composables. In remember the value is stored initially in it, when recomposition takes place the stored value is returned.While remember helps you retain state across recompositions. Updating takes place when a value paremeter changes.To do this we will create a Local State.
@Composable
fun LocalStateChange() {
var nameState: String by remember { mutableStateOf("") }
Column() {
Text(text = "Enter")
OutlinedTextField(
value = nameState,
onValueChange = { nameState = it },
label = { Text(text = "Name") })
}
}
Here the user can enter some text and in response the text field will be updated, this shows some interaction of user and events are being performed in this time. While remember helps you retain state across recompositions. mutablestateof() create mutable state which is of observale type in compose. Any change to its value the local variable (namestate) will change, Recomposition will take place and the values will be updated.
No State ✂️
@Composable
fun LocalStateChange() {
var name: String = "Ali"
Column() {
Text(text = "Enter")
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text(text = "Name") })
}
}
Recomposition would not take place here because a state is needed for it to make the composable aware of value that is needed to be observed to recompose the composables. Here, the name variable is a simple variable and thus has no capacity to change a state.
Stateful versus Stateless Composables
In above code snippets, we made stateful components which had states in the same composables, which was hard to resuse, test and was heavilly coupled too.
Stateless Composable
@Composable
fun LocalStateChange() {
var nameState by rememberSaveable { mutableStateOf("") }
LocalStateChange(
name = nameState,
onNameChange = { nameState = it })
}
@Composable
fun StateLessComposable(name: String, onNameChange: (String)-> Unit) {
Column() {
if (name.isNotEmpty()) {
Text(text = "Hello $name")
}
OutlinedTextField(
value = name,
onValueChange = onNameChange,
label = { Text("Name") })
}
}
In this above code, we can achieve the state change or play around events by making the composable states isloated. For this we need to have another composable that will be responsible for state change. In short making a stateless composable we will be moving state to a composable’s caller to make composable a stateless. This phenomenon is called State Hoisting which is performed for isolation of the composables.In state hoisting the general idea is to replace the state variable in the composable with two parameters value and onValueChange. Hence it makes the Composable isolated thus the code becomes easily testable and resuable.