Instruction 2
You learned about state hoisting, a powerful tool that enables you to create reusable stateless composable functions in Jetpack Compose.
With great power comes great responsibility! Here are some things you should keep in mind when implementing state hoisting.
Hoist to the Lowest Common Ancestor
The state should be hoisted to the lowest common parent of all the composables that use that state. As you write composables for your app UI, identify state variables multiple composables use. Lift the state variable to the closest parent composable that all those children share, the lowest common ancestor.
Consider the Wellness Club app’s profile screen. This screen displays user information, such as their name and email and has an edit button that lets the user edit their information.
The state to manage is the user’s name, email, and edit flag, which indicates whether the profile screen is in edit mode.
Both the DisplayItem() and EditButton() composables use the isEditing state variable:
@Composable
fun DisplayItem(itemValue: String, isEditing: Boolean) {
if (isEditing) {
TextField(value = itemValue, onValueChange = {})
} else {
Text(text = itemValue)
}
}
@Composable
fun EditButton(isEditing: Boolean, onEditingChanged: (Boolean) -> Unit) {
Button(onClick = {
onEditingChanged(!isEditing)
}) {
Text(text = if (isEditing) "Done" else "Edit")
}
}
The isEditing state variable can be hoisted to a composable responsible for handling edit functionality. This composable should be the closest parent that needs the isEditing state variable, for example, the ProfileContent() composable:
@Composable
fun ProfileContent(userData: UserData) {
var isEditing by remember { mutableStateOf(false) }
DisplayItem(itemValue = userData.name, isEditing = isEditing)
DisplayItem(itemValue = userData.email, isEditing = isEditing)
EditButton(isEditing = isEditing, onEditingChanged = {
isEditing = !isEditing
})
}
Remember:
Avoid over-hoisting. Don’t lift the state unnecessarily high in the hierarchy. Keep it at a level shared by multiple composables.
- For a very simple state used only by a single composable,
remembercan be sufficient.
For example:
@Composable
fun ProfileScreen(){
val userProfile by remember { mutableStateOf(UserData()) }
ProfileContent(userData = userProfile)
}
@Composable
fun ProfileContent(userData: UserData) {
var isEditing by remember { mutableStateOf(false) }
DisplayItem(itemValue = userData.name, isEditing = isEditing)
DisplayItem(itemValue = userData.email, isEditing = isEditing)
EditButton(isEditing = isEditing, onEditingChanged = {
isEditing = !isEditing
})
}
You did not hoist the isEditing flag to the ProfileScreen() composable.