如何避免Jetpack Compose中LeftControlContainers组件重复重组?
解决Compose中不必要的重组问题
我有一个仅含单个参数的可组合函数:
@Composable private fun LeftControlContainers( uiControl: EditorUiControl, ) { val controls = rememberUpdatedState(uiControl) val configState = remember { derivedStateOf { controls.value.controlMap[ArtifaktControls.CONFIGURATION.id] } } Column( modifier = Modifier.width(144.dp), verticalArrangement = Arrangement.spacedBy(Theme.spacingSystem.xxs) ) { key(configState.value, controls.value) { controls.value.Draw( uiElement = EditorUiDropDown(), elementId = ArtifaktControls.CONFIGURATION.id, ) } } }
EditorUiControl类定义如下:
class EditorUiControl( val defaultControlMap: ImmutableMap<String, NewEditorElement>, val controlMap: ImmutableMap<String, NewEditorElement>, val setControlValue: ( elementId: String, updatedEditorElement: NewEditorElement, changedValue: EditorElementValue<*>, ) -> Unit, ) { @Composable fun Draw( uiElement: EditorUiElement, elementId: String, modifier: Modifier = Modifier, filtrationMethod: FiltrationMethod = FiltrationMethod.SHUTDOWN, ) { uiElement.Draw( modifier = modifier, elementId = elementId, defaultControlMap = defaultControlMap, controlMap = controlMap, setControlValue = setControlValue, filtrationMethod = filtrationMethod, ) } }
目前遇到的问题是:每当任意单个项发生变化时,所有此类可组合函数都会触发重组,每次约200次。尝试过用remember和key包裹,但日志显示仍会每次重组,请问该如何避免?
问题根源分析
EditorUiControl不是稳定类:Compose判断是否重组的核心是参数稳定性,普通类默认以引用相等判断变化。如果父组件每次重组都创建新的EditorUiControl实例(哪怕内部属性一致),Compose都会认为参数更新,触发目标函数重组。key依赖冗余:原代码中key同时绑定configState.value和controls.value,只要EditorUiControl实例变化,不管目标配置项是否改动,都会触发key块内的重组。setControlValue可能不稳定:如果传递的是临时lambda,每次父组件重组都会生成新实例,即使逻辑相同,也会被判定为参数变化。
解决方案
1. 标记EditorUiControl为稳定类
给EditorUiControl添加@Stable注解,让Compose通过内容相等而非引用相等判断参数变化:
@Stable class EditorUiControl( val defaultControlMap: ImmutableMap<String, NewEditorElement>, val controlMap: ImmutableMap<String, NewEditorElement>, val setControlValue: ( elementId: String, updatedEditorElement: NewEditorElement, changedValue: EditorElementValue<*>, ) -> Unit, ) { // 保持原有Draw方法不变 }
注:
ImmutableMap本身是稳定类型,只要setControlValue是稳定引用(比如ViewModel的函数),这个类就能满足稳定类要求。
2. 精简key的依赖项
只保留真正影响UI的依赖,移除冗余的controls.value,确保只有目标配置项变化时才触发内部重组:
key(configState.value) { print("recomposed") controls.value.Draw( uiElement = EditorUiDropDown(), elementId = ArtifaktControls.CONFIGURATION.id, ) }
3. 拆分参数,避免传递整个对象
如果LeftControlContainers只用到EditorUiControl的部分属性,直接传递这些属性而非整个对象,缩小重组触发范围:
@Composable private fun LeftControlContainers( defaultControlMap: ImmutableMap<String, NewEditorElement>, controlMap: ImmutableMap<String, NewEditorElement>, setControlValue: (String, NewEditorElement, EditorElementValue<*>) -> Unit ) { val configElement = remember(controlMap) { controlMap[ArtifaktControls.CONFIGURATION.id] } Column( modifier = Modifier.width(144.dp), verticalArrangement = Arrangement.spacedBy(Theme.spacingSystem.xxs) ) { key(configElement) { EditorUiDropDown().Draw( elementId = ArtifaktControls.CONFIGURATION.id, defaultControlMap = defaultControlMap, controlMap = controlMap, setControlValue = setControlValue ) } } }
4. 确保setControlValue稳定
如果setControlValue是父组件传递的lambda,用remember缓存它,避免每次重组生成新实例:
// 父组件中传递时 val stableSetControlValue = remember { { elementId: String, element: NewEditorElement, value: EditorElementValue<*> -> // 原逻辑 } } LeftControlContainers(uiControl = EditorUiControl(..., setControlValue = stableSetControlValue))
或者直接使用ViewModel中的函数引用(ViewModel是稳定类,其方法引用也稳定)。
内容的提问来源于stack exchange,提问作者Denis Popkov
相关产品推荐
相关产品推荐

