Compose 中的附帶效應是指發(fā)生在可組合函數作用域之外的應用狀態(tài)的變化。例如刻蚯,當用戶點按一個按鈕時打開一個新屏幕,或者在應用未連接到互聯(lián)網時顯示一條消息。
Compose 中的附帶效應是指發(fā)生在可組合函數作用域之外的應用狀態(tài)的變化峻凫。將狀態(tài)更改為顯示/隱藏著陸屏幕的操作將發(fā)生在 onTimeout
回調中,由于在調用 onTimeout
之前我們需要先使用協(xié)程加載內容览露,因此狀態(tài)變化必須發(fā)生在協(xié)程的上下文中荧琼!
如需從可組合項內安全地調用掛起函數,請使用 LaunchedEffect
API差牛,該 API 會在 Compose 中觸發(fā)協(xié)程作用域限定的附帶效應命锄。
當 LaunchedEffect
進入組合時,它會啟動一個協(xié)程偏化,并將代碼塊作為參數傳遞脐恩。如果 LaunchedEffect
退出組合,協(xié)程將取消侦讨。
雖然接下來的代碼不正確驶冒,但讓我們看看如何使用此 API,并探討為什么下面的代碼是錯誤的搭伤。我們將在此步驟的后面調用 LandingScreen
可組合項只怎。
// home/LandingScreen.kt file
import androidx.compose.runtime.LaunchedEffect
import kotlinx.coroutines.delay
@Composable
fun LandingScreen(onTimeout: () -> Unit, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
// Start a side effect to load things in the background
// and call onTimeout() when finished.
// Passing onTimeout as a parameter to LaunchedEffect
// is wrong! Don't do this. We'll improve this code in a sec.
LaunchedEffect(onTimeout) {
delay(SplashWaitTime) // Simulates loading things
onTimeout()
}
Image(painterResource(id = R.drawable.ic_crane_drawer), contentDescription = null)
}
}
某些附帶效應 API(如 LaunchedEffect)會將可變數量的鍵作為參數,用于在其中一個鍵發(fā)生更改時重新開始效應怜俐。您發(fā)現(xiàn)錯誤了嗎身堡?我們不希望在此可組合函數的調用方傳遞不同的 onTimeout lambda 值時重啟 LaunchedEffect。這會讓 delay 再次啟動拍鲤,使得我們無法滿足相關要求贴谎。
如果 onTimeout
在附帶效應正在進行時發(fā)生變化,效應結束時不一定會調用最后一個 onTimeout
季稳。如需保證調用最后一個 onTimeout
擅这,請使用 rememberUpdatedState
API 記住 onTimeout
。此 API 會捕獲并更新最新值:
// home/LandingScreen.kt file
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import kotlinx.coroutines.delay
@Composable
fun LandingScreen(onTimeout: () -> Unit, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
// This will always refer to the latest onTimeout function that
// LandingScreen was recomposed with
val currentOnTimeout by rememberUpdatedState(onTimeout)
// Create an effect that matches the lifecycle of LandingScreen.
// If LandingScreen recomposes or onTimeout changes,
// the delay shouldn't start again.
LaunchedEffect(Unit) {
delay(SplashWaitTime)
currentOnTimeout()
}
Image(painterResource(id = R.drawable.ic_crane_drawer), contentDescription = null)
}
}
當長期存在的 lambda 或對象表達式引用在組合期間計算的參數或值時景鼠,您應使用 rememberUpdatedState仲翎,這在使用 LaunchedEffect 時可能很常見。