JetPack组成的副作用摘要
#初学者 #kotlin #android #jetpackcompose

您是否发现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()再次称为

非悬浮效应处理程序

对于非悬浮/非核心副作用,您可以使用这些非悬浮效果处理程序。

之后 之后,SideEffect()进入构图 时
方案 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

生产

produceStateLaunchedEffect()的糖语法。

下面的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>。因此,您看到上面的textval而不是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()或任何CorutineScopeawaitDispose()将不会被调用。例如,以下代码无法正常工作。

@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"
        }
    }
}

当更改value1value2时,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"。因此,它只是在这里浪费了不必要的资源。

快照流

snapShotFlowState<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