在Jetpack Compose中停止传递事件/UI-Action回调

图片

对于大多数屏幕而言,UDF对于事件的传递是低效的。

重要说明: 在本文中,假设您已经熟悉UDF(单向数据流)和MVI架构。

在Google的文档中,他们建议我们使用UDF来增加组件的可测试性和可重用性。例如…

@Composable
fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) {
TopAppBar(
title = {
Text(text = topAppBarText)
},
navigationIcon = {
IconButton(onClick = onBackPressed) {//}
},
)
}
通过向组件仅提供其所需的数据topAppBarText,并使用lambda函数onBackPressed来收集UI操作,我们使得该组件具有了可重用性且易于测试。这很棒,对吧?

然而,当您将该原则应用于整个屏幕时,您将很快发现需要从顶部组件一路传递20-30个事件回调到底部组件。让我举个例子 :)

@Composable
fun HomeScreen(
onItemClick: (String) -> Unit,
onSearchClick: () -> Unit,
/* 更多状态和回调 / ) { Home( onItemClick = onItemClick, onSearchClick = onSearchClick, / 更多状态和回调 / ) } @Composable private fun Home( onItemClick: (String) -> Unit, onSearchClick: () -> Unit, / 更多状态和回调 / ) { HomeContent( onItemClick = onItemClick, onSearchClick = onSearchClick, / 更多状态和回调 / ) } @Composable private fun HomeContent( onItemClick: (String) -> Unit, onSearchClick: () -> Unit, / 更多状态和回调 / ) { Row { Button(onClick = onSearchClick) { // } } HomeList( onItemClick = onItemClick, / 更多状态和回调 */
)
}
@Composable
private fun HomeList(onItemClick: (String) -> Unit) {
items.forEach { item ->
HomeListItem(
onItemClick = onItemClick
)
}
}
@Composable
private fun HomeListItem(onItemClick: (String) -> Unit) {
Button(onClick = { onItemClick(item) }) {
/
/
}
}
在这里,您会注意到我们需要像900次那样传递onItemClick/onSearchClick,这只是一个小例子,在实际项目中,相同回调的数量可能会让您觉得回到XML并不是一个坏主意 :)

另外,请查看Google示例中的此示例,请跟踪回调直至底部和顶部(HomeRoute和JetnewsNavGraph),以获取更多乐趣 🫠

UDF对于可重用/通用组件确实是一个很好的方法,但出于以下3个原因,它不适用于整个屏幕…

  1. 除非同一屏幕可重用于创建和更新操作,否则99%的屏幕都不可重用。
  2. 几乎所有屏幕的子组件都是特定于该屏幕,不能在其他地方重用。
  3. 由于开发人员懒惰,他们不会将组件提取为更小的组件,以避免传递所有这些回调的痛苦。结果,代码将变得难以阅读和维护。
    解决方案

解决方案很简单,如果您正在使用MVI,您已经为所有您的意图/操作拥有一个密封类,并在ViewModel中处理它们的onAction()/onIntent()/processIntents()函数。只需传递那个onAction()函数。

注意:即使您没有使用MVI,您仍然可以使用这个解决方案。

@Composable
fun HomeScreen(
onAction: (UiAcion) -> Unit
) {
Home(onAction = onAction)
}
@Composable
private fun Home(onAction: (UiAcion) -> Unit) {
HomeContent(onAction = onAction)
}
@Composable
private fun HomeContent(onAction: (UiAcion) -> Unit) {
Row {
Button(onClick = { onAction(UiAction.OnSearchClick) }) {
/*/ } } HomeList(onAction = onAction) } @Composable private fun HomeList(onAction: (UiAcion) -> Unit) { items.forEach { item -> HomeListItem( item = item, onAction = onAction, ) } } @Composable private fun HomeListItem( item: String, onAction: (UiAcion) -> Unit, ) { Button(onClick = { onAction(UiAcion.OnItemClick(item)) }) { // }
}
为什么这样更好?

  • 现在只有一个事件回调传递,而不是900次。
  • 开发时间更快,开发人员更快乐。
  • 您将鼓励您的团队将大型组件拆分为多个子组件,因为没有痛苦。
  • 调试变得更容易。比起浏览Action触发的位置,沿着树形结构逐个组件导航,您会更快地找到触发点,而不至于迷失方向。
  • 增强的代码可读性。
  • 仅传递函数引用,即viewModel::onAction,而不是多个lambda,将在性能上稍微更好,特别是当您在lambda内部使用不稳定的类(如viewModel)时。请查看此视频获取更多信息。
    如果您需要使您的屏幕或子组件可重用,只需进行重构。替换onAction为实际回调可能需要5到10分钟,但根据我的经验,这种情况很少,并不值得进行过度优化。

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/414042.html

(0)
联系我们
联系我们
分享本页
返回顶部