# UI State, Callbacks and Equality Pitfalls

DevFeed: [UI State, Callbacks and Equality Pitfalls](<https://devfeed.tech/articles/ui-state-callbacks-and-equality-pitfalls-25875.md>)

Original publisher: [Read original article](<http://lordraydenmk.github.io//2024/ui-state-callbacks/>)

Author: Stojan Anastasov

Published: 2024-11-30T00:00:00Z

Content type: tutorial

Language: en

Sources: [Stojan Anastasov's blog](<https://devfeed.tech/sources/stojan-anastasov-s-blog.md>)

Topics: [ui](<https://devfeed.tech/topics/ui.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Code](<https://devfeed.tech/topics/code.md>), [data](<https://devfeed.tech/topics/data.md>), [Compose](<https://devfeed.tech/topics/compose.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [data-class](<https://devfeed.tech/tags/data-class.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [pitfalls](<https://devfeed.tech/tags/pitfalls.md>), [sealed-class](<https://devfeed.tech/tags/sealed-class.md>), [state](<https://devfeed.tech/tags/state.md>), [ui](<https://devfeed.tech/tags/ui.md>)

## AI overview

This Kotlin article examines how callback functions embedded in UI state data classes can produce surprising equality results because function equality depends on reference identity. It explains the resulting update and performance issues in unidirectional data flow and discusses alternatives that keep action logic in the ViewModel or Presenter.

## Source excerpt

Yesterday a friend asked me to review his blog post on Function Properties in Data Classes are Code Smells. We then discussed how we would solve the issue in the context of UI state and callbacks. And I thought it might be useful to write it in form of a blog post. An example To better explain the problem, I'll start with a sample app I'm maintaining. It shows a list of superheroes from the Marvel API. See the screenshot below. Currently the UI state is modeled like: data class SuperheroViewEntity( val id: Long, val name: String, val imageUrl: HttpUrl ) Content( val superheroes: List<SuperheroViewEntity>, val copyright: String, ) // some states omitted for brevity A Naive Solution Let's say we have a new requirement, we want to add a favorite button to each superhero so we can keep track of our favorite superheroes. To do that we will introduce some functions: // ViewModel/Presenter fun onAddToFavorites(superheroId: Long) = TODO() fun onRemoveFromFavorites(superheroId: Long) = TODO() These functions can be part of the ViewModel/Presenter that delegates to a repository to store the IDs. Note: In this example, the ViewModel/Presenter use a single function per action the view can take. The technique described here also works with a single function + a sealed class for individual actions. To achieve this we might be tempted to update our SuperheroViewEntity to include: data class SuperheroViewEntity( val id: Long, val name: String, val imageUrl: HttpUrl, val onFavoriteClicked: () -> Unit ) // ViewModel fun Superhero.toViewEntity() = SuperheroViewEntity( id, name, imageUrl, if (favorite) { onRemoveFromFavorites(id) } else { onAddToFavorites(id) } ) // View layer Modifier.clickable { entity.onFavoriteClicked() } However this approach has some significant drawbacks. Functions in Kotlin are equal only if they are the same reference. val fa = { println(1) } val fb = { println(1) } println(fa == fb) // false, does the same job, but different reference val fc = fa println(fc =