Compose 两种 `derivedStateOf` 写法比较

在 Jetpack Compose 中,derivedStateOf 用来将一个或多个State转成另一个State。derivedStateOf{...} 的block中可以依赖其他State创建并返回一个DerivedState,当block中依赖的State发生变化时,会更新此DerivedState,依赖此DerivedState的所有Composable会因其变化而重组。

代码对比

写法一(无依赖项)

kotlin 复制代码
val list by remember { mutableStateOf(listOf("Michel", "Mike", "Jerry Black")) }
val targetCount by remember { mutableIntStateOf(10) }

val targetList by remember {
    derivedStateOf { list.filter { it.length >= targetCount } }
}

写法二(显式依赖项)

kotlin 复制代码
val list by remember { mutableStateOf(listOf("Michel", "Mike", "Jerry Black")) }
val targetCount by remember { mutableIntStateOf(10) }

val targetList by remember(list, targetCount) {
    derivedStateOf { list.filter { it.length >= targetCount } }
}

关键区别分析

特性 写法一 (无依赖项) 写法二 (显式依赖项)
依赖跟踪 自动跟踪计算中使用的所有状态 依赖项必须手动指定
重新创建时机 只在初始组合时创建一次 listtargetCount 变化时重新创建
内存效率 更高 - 对象只创建一次 较低 - 依赖变化时重新创建对象
计算触发 依赖变化时自动重新计算 依赖变化时自动重新计算
推荐场景 大多数场景 当派生状态依赖外部变量时
潜在风险 如果计算中使用了非状态变量可能出错 可能遗漏依赖项导致状态过期

详细说明

1. 依赖跟踪机制

  • 写法一derivedStateOf 会自动跟踪在其计算函数中访问的任何状态(listtargetCount)。当这些状态变化时,它会自动重新计算。

  • 写法二 :依赖项必须显式声明在 remember 的键中。如果遗漏了某个依赖项,当该依赖变化时不会触发重新计算。

2. 对象生命周期

kotlin 复制代码
// 写法一 - 只创建一次
val derivedState = remember {
    derivedStateOf { /* ... */ } // 只执行一次
}

// 写法二 - 依赖变化时重新创建
val derivedState = remember(key1, key2) {
    derivedStateOf { /* ... */ } // 当 key1 或 key2 变化时重新执行
}

3. 性能影响

  • 写法一 更高效,因为 derivedStateOf 实例只创建一次
  • 写法二 在依赖变化时会有额外开销:
    1. 创建新的 derivedStateOf 实例
    2. 丢弃旧实例
    3. 注册新的状态监听

4. 正确性考虑

两种写法在计算逻辑上结果相同,但写法二需要确保:

  1. 包含所有必要的依赖项
  2. 依赖项是稳定类型(避免不必要的重新创建)

何时使用哪种写法

✅ 优先使用写法一的情况:

  • 当派生状态只依赖于可组合函数内的状态时
  • 当依赖项是稳定的状态对象时
  • 大多数常见场景

⚠️ 考虑写法二的情况:

  1. 依赖外部参数时

    kotlin 复制代码
    @Composable
    fun FilteredList(externalParam: Int) {
        val list = // ...
        val filteredList by remember(list, externalParam) {
            derivedStateOf { 
                list.filter { it.length >= externalParam }
            }
        }
    }
  2. 依赖不稳定对象时

    kotlin 复制代码
    data class FilterCriteria(val minLength: Int) // 非稳定类型
    
    val filteredList by remember(criteria) {
        derivedStateOf { 
            list.filter { it.length >= criteria.minLength }
        }
    }

最佳实践建议

  1. 默认使用写法一

    kotlin 复制代码
    // 推荐 - 简洁高效
    val result by remember {
        derivedStateOf { computeValue(stateA, stateB) }
    }
  2. 当依赖外部变量时使用写法二

    kotlin 复制代码
    val result by remember(externalVar) {
        derivedStateOf { compute(state, externalVar) }
    }
  3. 避免不必要的重新创建

    kotlin 复制代码
    // 不推荐 - 依赖项变化会导致不必要的重新创建
    val result by remember(list, count) {
        derivedStateOf { /* 简单计算 */ }
    }
  4. 复杂派生状态使用自定义记忆

    kotlin 复制代码
    val complexResult by remember {
        derivedStateOf {
            // 计算密集型操作
            heavyComputation(list, count)
        }
    }

可视化重组行为

假设 list 变化但 targetCount 不变:

  • 写法一 :只重新计算 derivedStateOf,不重新创建对象
  • 写法二 :重新创建 derivedStateOf 对象并重新计算

结论

derivedStateOf 只能监听block内的State,一个非State类型数据的变化则可以通过remember的key进行监听

大多数情况下,写法一(无显式依赖项)是更好的选择

  • 更简洁
  • 更高效(避免不必要的对象创建)
  • 自动跟踪所有依赖
  • 符合 Compose 的设计哲学

仅当派生状态依赖外部参数或不稳定对象时,才需要使用写法二(显式依赖项)。在代码实践中,优先选择写法一可以创建更高效、更易维护的 Compose 应用。

相关推荐
4z331 天前
Jetpack Compose重组原理(一):快照系统如何精准追踪状态变化
前端·android jetpack
木易 士心2 天前
Android Jetpack Compose 从入门到精通
android·android jetpack
alexhilton2 天前
如何构建Android应用:深入探讨原则而非规则
android·kotlin·android jetpack
雨白2 天前
使用 Jetpack Compose 构建一个整洁架构笔记应用
android·android jetpack·mvvm
Lei活在当下8 天前
【业务场景架构实战】5. 使用 Flow 模式传递状态过程中的思考点
android·架构·android jetpack
天花板之恋9 天前
Compose状态管理
android jetpack
alexhilton10 天前
面向开发者的系统设计:像建筑师一样思考
android·kotlin·android jetpack
Lei活在当下11 天前
【业务场景架构实战】4. 支付状态分层流转的设计和实现
架构·android jetpack·响应式设计
天花板之恋11 天前
Compose之图片加载显示
android jetpack
消失的旧时光-194312 天前
Kotlinx.serialization 使用讲解
android·数据结构·android jetpack