您是否发现JetPack中的副作用令人困惑?我愿意。因此,我在此处记录了在此处使用副作用的摘要。
组合中的一个side effect是对应用程序范围之外发生的应用状态的一种更改。
要处理这些副作用,您可以使用效果处理程序。有两种效果处理程序。
悬浮效果处理程序 | 非悬浮效果处理程序 |
---|---|
启动eDeDeffect() | derposobleFect() |
rememencoroutinesCope() | sideffe() |
暂停效果处理程序
悬浮效应处理程序允许您使用coroutines进行副作用。
这是涵盖不同方案的摘要:
方案 | 启动effect() | rememencoroutinesCope() |
---|---|---|
从组合中的启动效果/coroutine? | 是 | 否 |
启动效果/coroutine在可组合的外部?例如。从回调 | 否 | 是 |
启动效果/coroutine时? |
LaunchedEffect() 进入构图 |
CoroutineScope.launch() 称为 |
何时取消效果/coroutine? |
LaunchedEffect() 叶构图,LaunchedEffect() 重新启动(效果的密钥更改) |
CoroutineScope 留下构图。 注意: 当重新启动Coroutine时,先前的Coroutine不会被取消
|
何时重新启动效果/coroutine? |
LaunchedEffect() 的键更改(当它仍处于组成中时) |
CoroutineScope.launch() 再次称为 |
非悬浮效应处理程序
对于非悬浮/非核心副作用,您可以使用这些非悬浮效果处理程序。
方案 | dorposobleFect() | sidefect() |
---|---|---|
从组合中的启动效果? | 是 | 是 |
在合并之外的启动效果?例如。从回调 | 否 | 否 |
何时启动效果? |
DisposableEffect() 进入构图,在当前构图完成 | 之后
当前构图完成 | 之后,
何时取消效果? | 效果无法取消,因为执行无法暂停(非悬浮函数) | 效果无法取消 - 与DisposableEffect() 相同
|
何时重新启动效果? |
DisposableEffect() 的键更改(仍处于组成中) |
SideEffect() 进入重新分配 - 每个重组都会触发 SideEffect() 运行 |
当onDispose() 称为? | 时
DisposableEffect() 叶构图,DisposableEffect() 重新启动(效果的键更改) 注意: 效果完成后,它不会触发 onDispose()
|
n/a |
各种副作用状态
这些是以上效应处理者的各种构图助手。
请记住
rememberUpdatedState
确保MutableState
始终使用最新值进行更新,而不是缓存初始构图值。
请参阅下面的注释(1),(2),(3),(4)和(5)。
@Composable
fun RememberUpdatedStated(value: String) {
val oldValue by remember { mutableStateOf(value) }
val newValue by rememberUpdatedState(value)
// (2) LaunchedEffect is skipped during the second recomposition
// when value is changed/updated
LaunchedEffect(true) {
// (1) let's assume value is updated with a new value within 1 second delay
delay(1000)
// Access value, oldvalue and newValue here
// (3) value is the initial value when LaunchedEffect is first called
// (4) oldValue is the initial value from first composition
// (5) newValue is the new value from second recomposition
}
}
仅
newValue
具有第二个重新组件的最新更新的value
如果您查看rememberUpdatedState
实现,则在调用或重新组件时将新值应用于MutableState
。
@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
mutableStateOf(newValue)
}.apply { value = newValue }
从技术上讲,
val newValue by rememberUpdatedState(value)
等于
var newValue by remember { mutableStateOf(value) }
newValue = value
生产
produceState
是LaunchedEffect()
的糖语法。
下面的LaunchedEffect()
可以按照
写入
@Composable
fun DemoLaunchedEffect(){
var text by remember { mutableStateOf("")}
LaunchedEffect(true) {
repeat(10) { count ->
delay(1000)
text = count.toString()
}
}
}
produceState()
下面:
@Composable
fun DemoProduceState(){
val text by produceState(initialValue ="") {
repeat(10) { count ->
delay(1000)
value = count.toString()
}
}
}
produceState
的一个小区别是它产生State<T>
而不是MutableState<T>
。因此,您看到上面的text
用val
而不是var
声明。
produceState
可以做的另一件事是awaitDispose()
功能,它使您可以检测到produceState
何时离开组合物。这类似于DisposableEffect()
的onDispose()
@Composable
fun DemoProduceStateAwaitDispose(){
val text by produceState(initialValue ="") {
val job = MainScope().launch {
repeat(10) { count ->
delay(1000)
value = count.toString()
}
}
awaitDispose {
job.cancel()
}
}
}
但是,要使用
awaitDispose()
,您必须手动使用CoroutineScope
启动Coroutine。以下示例使用MainScope()
。
没有MainScope()
或任何CorutineScope
,awaitDispose()
将不会被调用。例如,以下代码无法正常工作。
@Composable
fun DemoProduceStateAwaitDispose(){
val text by produceState(initialValue ="") {
repeat(10) { count ->
delay(1000)
value = count.toString()
}
// Won't work - awaitDispose() won't be called
awaitDispose {
job.cancel()
}
}
}
出于某种原因,该要求未记录。我花了一段时间才能找出
awaitDispose()
需要您手动启动Coroutine以使其正常工作。
派生
不确定为什么在副作用下将其分类,而derivedStateOf()
的作用是将多个状态组合到一个State
中。
@Composable
fun DemoDerivedStateOf() {
var value1 by remember { mutableStateOf(true) }
var value2 by remember { mutableStateOf(false) }
val derivedValue by remember(value1) {
derivedStateOf {
"value1: $value1 + value2: $value2"
}
}
}
当更改value1
或value2
时,derivedValue
会更新。需要remember(value1)
,以便在重新组合过程中跳过derivedStateOf()
。
如果您删除remember(value1)
作为以下内容,则一切仍然正常工作。
@Composable
fun DemoDerivedStateOf() {
var value1 by remember { mutableStateOf(true) }
var value2 by remember { mutableStateOf(false) }
val derivedValue by derivedStateOf {
"value1: $value1 + value2: $value2"
}
}
但是,在每个重新组件期间,都执行此行"value1: $value1 + value2: $value2"
。因此,它只是在这里浪费了不必要的资源。
快照流
snapShotFlow
将State<T>
转换为Flow<T>
。这是一个示例:
@Composable
fun DemoSnapShotFlow(){
var textState = remember { mutableStateOf("") }
LaunchedEffect(textState) {
// Convert State<T> to Flow<T>
val flow = snapshotFlow { textState.value }
// Ensure flow doesn't emit the same value twise
flow.distinctUntilChanged()
// Collect the flow
flow.collect { text ->
Log.d("[SnapShotFlow]", "collecting flow value: $text")
}
}
}
有关收集流的更多信息,请参阅以下文章:
结论
我只是只记录这些副作用处理程序的用法及其行为。但是,我还不知道如何有效使用它们。有时我确实觉得这是令人困惑的。 :)
源代码
这是我在JetPack组成中扮演副作用的实验守则。所以这里可能有点混乱。
github存储库:side effect
最初出版于https://vtsen.hashnode.dev 。