The refactors I did to stop my Jetpack Compose LazyColumn from constantly recomposing

RMAG news

Table of contents

Resources
The problem I was facing
The data class fix
Unstable Lambdas
Stabilizing the Lambdas

My app on the Google play store

The app

Resources

Gotchas in Jetpack Compose Recomposition

TLDR(too long didn’t read)

read the Gotchas in Jetpack Compose Recomposition article for a deeper understanding
don’t use var for any of your data classes
unstable lambdas are lambdas that contain methods from a View Model or are suspending
use remember(){} to stabilize lambdas that are used inside of the LazyColumn items.

The problem I was facing

I have my real time chat application that connects to Twitch’s web server via web-socket. UI demonstration, HERE. The problem I was facing was that the entire LazyColumn would recompose every time a new chat message was added to the UI. Which as you can imagine causes a lot of unnecessary re-compositions when 50,000 users are all chatting at the same time.

I ran into 2 main problems that were causing recompositions, 1)unstable Data class, 2) unstable functions

The data class fix

if you are passing any data classes into a LazyColumn they should always use val over var.

//CORRECT
data class Test(
val testingOne:Boolean,
val testingTwo:Boolean
)

//WRONG
data class Test(
var testingOne:Boolean,
var testingTwo:Boolean
)

Adding var will cause compose to view the Test data class as Unstable meaning that it will not be able to skip any sort of re-compositions

Unstable lambdas

As it turns out, methods that come from a viewModel or are suspending are also considered unstable. You can read more about this in the Gotchas in Jetpack Compose Recomposition article. Which means given this code:

@Composable
fun TestingOne(streamViewModel: StreamViewModel){
val listOfString = mutableListOf(“one”,“two”,“three”)
TestingLazyColumn(
functionFromViewMode={streamViewModel.updateMethod()},
stringList =listOfString
)

}
@Composable
fun TestingLazyColumn(
functionFromViewMode:() ->Unit,
stringList:List<String>
){
LazyColumn(){
items(stringList){
Button(onClick ={functionFromViewMode()}) {
Text(“Click me”)
}
}
}
}

Any time the listOfString is updated, every single one of the items in the string list will be recomposed. If you have 100,000+ items, that is going to cause some jank.

Stabilizing unstable lambdas

There are a couple of ways that we can stabalize these lambdas, again, you can read about all the ways inside of the Gotchas in Jetpack Compose Recomposition article. I have used the remember method:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun TestingOne(streamViewModel: StreamViewModel,bottomModalState: ModalBottomSheetState){

val listOfString = mutableListOf(“one”,“two”,“three”)
val updateMethod:()->Unit =remember(streamViewModel) { {
streamViewModel.updateMethod()
} }
//example of stabilizing suspending function:
val scope = rememberCoroutineScope()
val showClickedUserBottomModal:()->Unit =remember(bottomModalState) { {
scope.launch {
bottomModalState.show()
}
} }
//example of method with multiple parameters:
val updateClickedUser🙁String,String,Boolean,Boolean)->Unit = remember(streamViewModel) { { username, userId, banned, isMod ->
streamViewModel.updateClickedChat(
username,
userId,
banned,
isMod
)
} }

TestingLazyColumn(
functionFromViewMode={updateMethod()},
stringList =listOfString
)

}
@Composable
fun TestingLazyColumn(
functionFromViewMode:() ->Unit,
stringList:List<String>
){
LazyColumn(){
items(stringList){
Button(onClick ={functionFromViewMode()}) {
Text(“Click me”)
}
}
}
}

Long story short, remember() allows the compose compiler to skip the recomposition

Conclusion

Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *