6

In Jetpack Compose we see all built in composable have flattened inputs, is it intended? Or wrapping too many inputs with data class (which is good and clean practice) has the same performance?

Consider this sample

data class SettingsItemEntity(
    val title: String,
    val description: String? = null,
    @DrawableRes val imageResId: Int? = null,
    val isChecked: Boolean? = null,
    val showDivider: Boolean = true,
    val onItemClicked: () -> Unit = {},
    val onCheckedChange: (isChecked: Boolean) -> Unit = {},
    val buttonLabel: String? = null,
    val onButtonClicked: () -> Unit = {},
)

@Composable
fun SettingsItem(
    entity: SettingsItemEntity,
    modifier: Modifier = Modifier
) { 
...
}

Would be better to send inputs as data class or flattened inputs in performance(recomposition)? Or has the equal result?

In other way, when we send a data class to composable function, and we just change one member of it, does it cause that the whole function recomposed? Or recomposition wise, it has same performance as when using flattened inputs?

Thank you in advance for your help.

  • 1
    I think there is no problem, Use Data Class as View State, like update loading, passing model to view, it's better to move on Click actions out of data class and pass your action from composable function, it's better your view state to be in the first composable function and pass your state to secondary composable function. – OneDev Feb 17 '22 at 10:59
  • 1
    Thanks for your answer @OneDev, Do you mean with this part State Hoisting? -> "it's better to move on Click actions out of data class and pass your action from composable function, it's better your view state to be in the first composable function and pass your state to secondary composable function" – Sepideh Vatankhah Feb 17 '22 at 11:05
  • Yes, exactly. use kotlin flows (state flow) to handle view state. – OneDev Feb 17 '22 at 11:37
  • 1
    Not sure I understand your question. You want to know whether there is a performance difference between using a data class as a parameter instead of passing in a bunch of paramerters? Is so, the performance will probably be very negligible. Java still needs to push the parameters onto the stack. Pushing 9 parameters instead of a single data class parameter will take 9 times longer. But we are talking micrroseconds here and it's highly unlikely you will ever need that kind of performance increase. Stick with the data class if you can. It's cleaner. – Johann Feb 17 '22 at 12:33
  • Question updated @Johann – Sepideh Vatankhah Feb 17 '22 at 12:51
  • 1
    If you use an object (such as a data class) and change a member value, it will not cause a recomposition because the parameter itself, which is really a reference to an object has not changed. Same applies if the parameter is a high order function. However, if you were to provide the composable function a completely new object, it would recompose. – Johann Feb 17 '22 at 14:11
  • @Johann I didn't get this part "However, if you were to provide the composable function a completely new object, it would recompose", what do you mean exactly? – Sepideh Vatankhah Feb 17 '22 at 14:15
  • If your viewmodel create a new SettingsItemEntity object when SettingsItem is called, it will recompose because you are passing in a different parameter value. An object is a reference and every time you create a new object, you get a new reference. – Johann Feb 17 '22 at 15:46
  • @Johann I disagree that passing data classes instead of flat inputs is cleaner. I wouldn't want to work in a codebase where every other composable function has abstracted its parameters into an unique data class. The verbosity is the same, you still have to declare all those parameters, except now you have data classes everywhere and you have to instantiate one eveytime you call that function. – jpegoraro Feb 17 '22 at 23:23
  • @hahmraro I think you're assuming too much. Most apps retrieve things from the backend like user data and such. These are usually stored in objects. Passing these objects to functions as a parameter is totally acceptable and far less work than splitting it apart into individual parameters. Furthermore, an object allows you to provide new object members to the function without breaking the parameters. – Johann Feb 18 '22 at 05:37
  • @Johann the viewmodel instantiates the data class just the first time, not every time – Sepideh Vatankhah Feb 18 '22 at 07:56
  • I know. What's your point? Where did I say the viewmodel instantiates every time? – Johann Feb 18 '22 at 08:35
  • 1
    This part "If your viewmodel create a new SettingsItemEntity object when SettingsItem is called", so according that it doesn't change every time and only instantiate first time in viewmodel, recomposition doesn't happen for whole part of composable with changing each member of data class, right? – Sepideh Vatankhah Feb 18 '22 at 08:50

1 Answers1

1

I think the performance impact is minimal from choosing one or another. I think that the Compose developers just didn't think data classes as parameter where the best route, and I agree.

I don't see how this:

  data class SettingsItemEntity(
    val title: String,
    val description: String? = null,
    @DrawableRes val imageResId: Int? = null,
    val isChecked: Boolean? = null,
    val showDivider: Boolean = true,
    val onItemClicked: () -> Unit = {},
    val onCheckedChange: (isChecked: Boolean) -> Unit = {},
    val buttonLabel: String? = null,
    val onButtonClicked: () -> Unit = {},
)

@Composable
fun SettingsItem(
    entity: SettingsItemEntity,
    modifier: Modifier = Modifier
) { 
...
}

Is better than this:

@Composable
fun SettingsItem(
    title: String,
    description: String? = null,
    @DrawableRes imageResId: Int? = null,
    isChecked: Boolean? = null,
    showDivider: Boolean = true,
    onItemClicked: () -> Unit = {},
    onCheckedChange: (isChecked: Boolean) -> Unit = {},
    buttonLabel: String? = null,
    onButtonClicked: () -> Unit = {},
    modifier: Modifier = Modifier
) { 
...
}

Having a data class involved in every interaction with a compose function with a lengthy number of arguments just adds unnecessary complexity to your code. It adds more obstacles to reading documentation, calling the function, and overall understanding of the codebase, for the single benefit of having less code inside the parenthesis of the function declaration (but more code everywhere else).

jpegoraro
  • 315
  • 2
  • 10