ผลข้างเคียงคือการเปลี่ยนแปลงสถานะของแอปที่เกิดขึ้นนอกขอบเขตของฟังก์ชันคอมโพสิเบิล เนื่องจากวงจรและพร็อพเพอร์ตี้ของคอมโพสิเบิล เช่น การจัดเรียงใหม่ที่ไม่คาดคิด การจัดเรียงใหม่ของคอมโพสิเบิลตามลําดับที่ต่างกัน หรือการจัดเรียงใหม่ที่ทิ้งได้ คอมโพสิเบิลจึงควรไม่มีผลข้างเคียง
อย่างไรก็ตาม บางครั้งก็จำเป็นต้องใช้ผลข้างเคียง เช่น เพื่อเรียกเหตุการณ์ที่เกิดขึ้นเพียงครั้งเดียว เช่น การแสดง Snackbar หรือไปยังหน้าจออื่นตามเงื่อนไขสถานะบางอย่าง การดําเนินการเหล่านี้ควรเรียกใช้จากสภาพแวดล้อมที่มีการควบคุมซึ่งทราบวงจรชีวิตของคอมโพสิเบิล ในหน้านี้ คุณจะได้เรียนรู้เกี่ยวกับ API ที่มีผลข้างเคียงต่างๆ ที่ Jetpack Compose มีให้
กรณีการใช้งานสถานะและผล
ตามที่อธิบายไว้ในเอกสารการคิดใน Compose คอมโพสิเบิลไม่ควรก่อให้เกิดผลข้างเคียง เมื่อต้องการเปลี่ยนแปลงสถานะของแอป (ตามที่อธิบายไว้ในเอกสารเอกสารประกอบเกี่ยวกับการจัดการสถานะ) คุณควรใช้ Effect API เพื่อให้ผลข้างเคียงเหล่านั้นทำงานในลักษณะที่คาดการณ์ได้
เนื่องจากเอฟเฟกต์ต่างๆ ที่เปิดขึ้นในเครื่องมือเขียนมีหลากหลาย จึงอาจทำให้คุณใช้เอฟเฟกต์มากเกินไปได้ ตรวจสอบว่างานที่ทําในไฟล์ดังกล่าวเกี่ยวข้องกับ UI และไม่ได้รบกวนการไหลของข้อมูลแบบทิศทางเดียวตามที่อธิบายไว้ในเอกสารประกอบเกี่ยวกับการจัดการสถานะ
LaunchedEffect
: เรียกใช้ฟังก์ชันที่ระงับชั่วคราวในขอบเขตของคอมโพสิเบิล
หากต้องการทำงานตลอดอายุของคอมโพสิเบิลและสามารถเรียกใช้ฟังก์ชันที่ระงับ ให้ใช้คอมโพสิเบิล LaunchedEffect
เมื่อ LaunchedEffect
เข้าสู่การคอมโพสิชัน ระบบจะเปิดใช้โคโริวทีนที่มีบล็อกโค้ดที่ส่งผ่านเป็นพารามิเตอร์ ระบบจะยกเลิก coroutine หาก LaunchedEffect
ออกจากการคอมโพสิชัน หาก LaunchedEffect
ได้รับการคอมโพสิชันใหม่ด้วยคีย์อื่น (ดูส่วนการเริ่มผลกระทบใหม่ด้านล่าง) ระบบจะยกเลิก coroutine ที่มีอยู่และเปิดใช้งานฟังก์ชันการระงับใหม่ใน coroutine ใหม่
ตัวอย่างเช่น นี่คือภาพเคลื่อนไหวที่กะพริบค่าอัลฟ่าโดยมีความล่าช้าที่กำหนดค่าได้
// Allow the pulse rate to be configured, so it can be sped up if the user is running // out of time var pulseRateMs by remember { mutableStateOf(3000L) } val alpha = remember { Animatable(1f) } LaunchedEffect(pulseRateMs) { // Restart the effect when the pulse rate changes while (isActive) { delay(pulseRateMs) // Pulse the alpha every pulseRateMs to alert the user alpha.animateTo(0f) alpha.animateTo(1f) } }
ในโค้ดด้านบน ภาพเคลื่อนไหวใช้ฟังก์ชันการระงับ
delay
เพื่อรอตามระยะเวลาที่กำหนด จากนั้นจะแสดงภาพเคลื่อนไหวของค่าอัลฟาทีละขั้นจาก 0 กลับไปที่ 0 โดยใช้ animateTo
ซึ่งจะทําซ้ำตลอดอายุการใช้งานของคอมโพสิเบิล
rememberCoroutineScope
: รับขอบเขตที่รับรู้การคอมโพสิชันเพื่อเปิดใช้ coroutine นอก composable
เนื่องจาก LaunchedEffect
เป็นฟังก์ชันคอมโพสิเบิล จึงใช้ได้เฉพาะภายในฟังก์ชันคอมโพสิเบิลอื่นๆ เท่านั้น หากต้องการเปิดใช้ coroutine นอกคอมโพสิเบิล แต่กําหนดขอบเขตไว้เพื่อให้ยกเลิกโดยอัตโนมัติเมื่อออกจากคอมโพสิชัน ให้ใช้ rememberCoroutineScope
นอกจากนี้ ให้ใช้ rememberCoroutineScope
ทุกครั้งที่คุณต้องควบคุมวงจรชีวิตของ coroutine อย่างน้อย 1 รายการด้วยตนเอง เช่น การยกเลิกภาพเคลื่อนไหวเมื่อมีเหตุการณ์ของผู้ใช้เกิดขึ้น
rememberCoroutineScope
คือฟังก์ชันคอมโพสิเบิลที่แสดงผล CoroutineScope
ซึ่งเชื่อมโยงกับจุดของคอมโพสิชันที่มีการเรียกใช้ ระบบจะยกเลิกขอบเขตเมื่อการโทรออกจากการคอมโพสิชัน
จากตัวอย่างก่อนหน้านี้ คุณสามารถใช้โค้ดนี้เพื่อแสดง Snackbar
เมื่อผู้ใช้แตะ Button
@Composable fun MoviesScreen(snackbarHostState: SnackbarHostState) { // Creates a CoroutineScope bound to the MoviesScreen's lifecycle val scope = rememberCoroutineScope() Scaffold( snackbarHost = { SnackbarHost(hostState = snackbarHostState) } ) { contentPadding -> Column(Modifier.padding(contentPadding)) { Button( onClick = { // Create a new coroutine in the event handler to show a snackbar scope.launch { snackbarHostState.showSnackbar("Something happened!") } } ) { Text("Press me") } } } }
rememberUpdatedState
: อ้างอิงค่าในเอฟเฟกต์ที่ไม่ควรเริ่มต้นใหม่หากค่ามีการเปลี่ยนแปลง
LaunchedEffect
จะรีสตาร์ทเมื่อพารามิเตอร์หลักรายการใดรายการหนึ่งมีการเปลี่ยนแปลง อย่างไรก็ตาม ในบางสถานการณ์ คุณอาจต้องการบันทึกค่าในเอฟเฟกต์ที่หากมีการเปลี่ยนแปลง คุณไม่ต้องการให้เอฟเฟกต์เริ่มทำงานอีกครั้ง ในการดําเนินการนี้ คุณต้องใช้ rememberUpdatedState
เพื่อสร้างการอ้างอิงถึงค่านี้ ซึ่งสามารถบันทึกและอัปเดตได้ วิธีนี้มีประโยชน์สำหรับเอฟเฟกต์ที่มีการดำเนินการที่ใช้เวลานาน ซึ่งอาจทําให้สร้างใหม่และเริ่มใหม่ได้ยากหรือมีค่าใช้จ่ายสูง
ตัวอย่างเช่น สมมติว่าแอปของคุณมี LandingScreen
ที่หายไปหลังจากผ่านไประยะหนึ่ง แม้ว่าจะมีการคอมโพสิชัน LandingScreen
ใหม่ แต่เอฟเฟกต์ที่รอเวลาสักครู่และแจ้งว่าเวลาผ่านไปแล้วไม่ควรเริ่มต้นใหม่
@Composable fun LandingScreen(onTimeout: () -> Unit) { // 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, the delay shouldn't start again. LaunchedEffect(true) { delay(SplashWaitTimeMillis) currentOnTimeout() } /* Landing screen content */ }
หากต้องการสร้างเอฟเฟกต์ที่ตรงกับวงจรของเว็บไซต์ที่เรียกใช้ ระบบจะส่งค่าคงที่ที่ไม่เปลี่ยนแปลง เช่น Unit
หรือ true
เป็นพารามิเตอร์ ในโค้ดด้านบนมีการใช้ LaunchedEffect(true)
onTimeout
ต้องรวมเข้ากับฟังก์ชัน rememberUpdatedState
เพื่อให้แน่ใจว่า onTimeout
lambda always มีค่าล่าสุดที่ LandingScreen
คอมโพสใหม่แล้ว
State
, currentOnTimeout
ที่แสดงผลในโค้ดควรใช้ในเอฟเฟกต์
DisposableEffect
: เอฟเฟกต์ที่ต้องล้าง
สำหรับผลข้างเคียงที่ต้องล้างออกหลังจากคีย์มีการเปลี่ยนแปลง หรือหากคอมโพสิเบิลออกจากการคอมโพสิชัน ให้ใช้ DisposableEffect
หากคีย์ DisposableEffect
มีการเปลี่ยนแปลง คอมโพสิเบิลจะต้องทิ้ง (ล้างข้อมูล) เอฟเฟกต์ปัจจุบัน และรีเซ็ตโดยการเรียกใช้เอฟเฟกต์อีกครั้ง
ตัวอย่างเช่น คุณอาจต้องการส่งเหตุการณ์ Analytics โดยอิงตามเหตุการณ์ Lifecycle
โดยใช้ LifecycleObserver
หากต้องการฟังเหตุการณ์เหล่านั้นในคอมโพสิท ให้ใช้ DisposableEffect
เพื่อลงทะเบียนและยกเลิกการลงทะเบียนผู้สังเกตการณ์เมื่อจำเป็น
@Composable fun HomeScreen( lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current, onStart: () -> Unit, // Send the 'started' analytics event onStop: () -> Unit // Send the 'stopped' analytics event ) { // Safely update the current lambdas when a new one is provided val currentOnStart by rememberUpdatedState(onStart) val currentOnStop by rememberUpdatedState(onStop) // If `lifecycleOwner` changes, dispose and reset the effect DisposableEffect(lifecycleOwner) { // Create an observer that triggers our remembered callbacks // for sending analytics events val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_START) { currentOnStart() } else if (event == Lifecycle.Event.ON_STOP) { currentOnStop() } } // Add the observer to the lifecycle lifecycleOwner.lifecycle.addObserver(observer) // When the effect leaves the Composition, remove the observer onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } /* Home screen content */ }
ในโค้ดด้านบน เอฟเฟกต์จะเพิ่ม observer
ลงใน
lifecycleOwner
หาก lifecycleOwner
มีการเปลี่ยนแปลง ระบบจะทิ้งเอฟเฟกต์และเริ่มใหม่ด้วย lifecycleOwner
ใหม่
DisposableEffect
ต้องมีประโยค onDispose
เป็นคำสั่งสุดท้ายในบล็อกโค้ด มิฉะนั้น IDE จะแสดงข้อผิดพลาดเวลาสร้าง
SideEffect
: เผยแพร่สถานะการเขียนโค้ดไปยังโค้ดที่ไม่ใช่การเขียนโค้ด
หากต้องการแชร์สถานะการคอมโพสิชันกับออบเจ็กต์ที่ไม่ได้จัดการโดยคอมโพสิชัน ให้ใช้ SideEffect
คอมโพสิเบิล การใช้ SideEffect
เป็นการรับประกันว่าเอฟเฟกต์จะทำงานหลังจากการจัดเรียงใหม่สำเร็จทุกครั้ง ในทางกลับกัน การใช้เอฟเฟกต์ก่อนที่จะรับประกันได้ว่าการจัดเรียงใหม่สำเร็จนั้นไม่ถูกต้อง ซึ่งจะเป็นกรณีเมื่อเขียนเอฟเฟกต์ในคอมโพสิเบิลโดยตรง
เช่น ไลบรารีข้อมูลวิเคราะห์อาจช่วยให้คุณแบ่งกลุ่มประชากรผู้ใช้ได้โดยแนบข้อมูลเมตาที่กําหนดเอง ("พร็อพเพอร์ตี้ผู้ใช้" ในตัวอย่างนี้) ไปกับเหตุการณ์การวิเคราะห์ที่ตามมาทั้งหมด หากต้องการสื่อสารประเภทผู้ใช้ของผู้ใช้ปัจจุบันกับคลังข้อมูลวิเคราะห์ ให้ใช้ SideEffect
เพื่ออัปเดตค่า
@Composable fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics { val analytics: FirebaseAnalytics = remember { FirebaseAnalytics() } // On every successful composition, update FirebaseAnalytics with // the userType from the current User, ensuring that future analytics // events have this metadata attached SideEffect { analytics.setUserProperty("userType", user.userType) } return analytics }
produceState
: แปลงสถานะที่ไม่อยู่ในโหมดคอมโพซให้เป็นสถานะคอมโพซ
produceState
จะเปิดใช้งาน coroutine ที่มีขอบเขตเป็นองค์ประกอบ ซึ่งสามารถพุชค่าไปยัง State
ที่แสดงผล ใช้เพื่อแปลงสถานะที่ไม่อยู่ในคอมโพสิชันให้เป็นสถานะคอมโพสิชัน เช่น การนำสถานะภายนอกที่ขับเคลื่อนโดยการสมัครใช้บริการ เช่น Flow
, LiveData
หรือ RxJava
มาใช้ในคอมโพสิชัน
ระบบจะเปิดใช้งาน Producer เมื่อ produceState
เข้าสู่ Composition และจะยกเลิกเมื่อออกจาก Composition State
ที่แสดงผลจะรวมค่าต่างๆ เข้าด้วยกัน การตั้งค่าค่าเดียวกันจะไม่ทริกเกอร์การจัดองค์ประกอบใหม่
แม้ว่า produceState
จะสร้าง coroutine แต่ก็สามารถใช้ในการสังเกตแหล่งข้อมูลที่ไม่หยุดทำงานได้เช่นกัน หากต้องการยกเลิกการติดตามแหล่งที่มานั้น ให้ใช้ฟังก์ชัน awaitDispose
ตัวอย่างต่อไปนี้แสดงวิธีใช้ produceState
เพื่อโหลดรูปภาพจากเครือข่าย ฟังก์ชันคอมโพสิเบิล loadNetworkImage
จะแสดงผล State
ที่ใช้ได้ในคอมโพสิเบิลอื่นๆ
@Composable fun loadNetworkImage( url: String, imageRepository: ImageRepository = ImageRepository() ): State<Result<Image>> { // Creates a State<T> with Result.Loading as initial value // If either `url` or `imageRepository` changes, the running producer // will cancel and will be re-launched with the new inputs. return produceState<Result<Image>>(initialValue = Result.Loading, url, imageRepository) { // In a coroutine, can make suspend calls val image = imageRepository.load(url) // Update State with either an Error or Success result. // This will trigger a recomposition where this State is read value = if (image == null) { Result.Error } else { Result.Success(image) } } }
derivedStateOf
: แปลงออบเจ็กต์สถานะอย่างน้อย 1 รายการเป็นสถานะอื่น
ในคอมโพสิท การจัดองค์ประกอบใหม่จะเกิดขึ้นทุกครั้งที่ออบเจ็กต์สถานะที่สังเกตได้หรืออินพุตแบบคอมโพสิทมีการเปลี่ยนแปลง ออบเจ็กต์สถานะหรืออินพุตอาจเปลี่ยนแปลงบ่อยกว่าที่ UI จำเป็นต้องอัปเดตจริง ซึ่งส่งผลให้มีการจัดองค์ประกอบใหม่โดยไม่จำเป็น
คุณควรใช้ฟังก์ชัน derivedStateOf
เมื่ออินพุตของคอมโพสิเบิลมีการเปลี่ยนแปลงบ่อยกว่าที่คุณต้องคอมโพสิเบิลใหม่ กรณีนี้มักเกิดขึ้นเมื่อมีการเปลี่ยนแปลงบางอย่างบ่อยครั้ง เช่น ตำแหน่งการเลื่อน แต่คอมโพสิเบิลต้องตอบสนองต่อการเปลี่ยนแปลงเมื่อข้ามเกณฑ์ที่กำหนดเท่านั้น derivedStateOf
จะสร้างออบเจ็กต์สถานะการคอมโพสิทใหม่ที่คุณสังเกตได้ ซึ่งจะอัปเดตเฉพาะเท่าที่คุณต้องการ วิธีนี้ทํางานคล้ายกับโอเปอเรเตอร์ distinctUntilChanged()
ของ Kotlin Flows
การใช้งานที่ถูกต้อง
ข้อมูลโค้ดต่อไปนี้แสดงกรณีการใช้งานที่เหมาะสมสําหรับ derivedStateOf
@Composable // When the messages parameter changes, the MessageList // composable recomposes. derivedStateOf does not // affect this recomposition. fun MessageList(messages: List<Message>) { Box { val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } // Show the button if the first visible item is past // the first item. We use a remembered derived state to // minimize unnecessary compositions val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } AnimatedVisibility(visible = showButton) { ScrollToTopButton() } } }
ในข้อมูลโค้ดนี้ firstVisibleItemIndex
จะเปลี่ยนแปลงทุกครั้งที่รายการแรกที่มองเห็นได้เปลี่ยนแปลง เมื่อคุณเลื่อนค่าจะกลายเป็น 0
, 1
, 2
, 3
, 4
, 5
ฯลฯ
แต่จะต้องจัดเรียงใหม่เฉพาะในกรณีที่ค่ามากกว่า 0
ความถี่การอัปเดตที่ไม่ตรงกันนี้หมายความว่ากรณีการใช้งานนี้เหมาะกับ derivedStateOf
การใช้งานที่ไม่ถูกต้อง
ความผิดพลาดที่พบบ่อยคือการคิดว่าเมื่อคุณรวมออบเจ็กต์สถานะของ Compose 2 รายการ คุณควรใช้ derivedStateOf
เนื่องจากคุณกําลัง "ดึงข้อมูลสถานะ" อย่างไรก็ตาม การดำเนินการนี้เป็นเพียงขั้นตอนเพิ่มเติมที่ไม่จำเป็นเท่านั้น ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
// DO NOT USE. Incorrect usage of derivedStateOf. var firstName by remember { mutableStateOf("") } var lastName by remember { mutableStateOf("") } val fullNameBad by remember { derivedStateOf { "$firstName $lastName" } } // This is bad!!! val fullNameCorrect = "$firstName $lastName" // This is correct
ในสnippet นี้ fullName
ต้องอัปเดตบ่อยเท่ากับ firstName
และ
lastName
ดังนั้นจึงไม่มีการคอมโพสใหม่เกินและไม่จำเป็นต้องใช้
derivedStateOf
snapshotFlow
: แปลงสถานะของ Compose เป็นโฟลว์
ใช้ snapshotFlow
เพื่อแปลงออบเจ็กต์ State<T>
เป็นโฟลว์แบบเย็น snapshotFlow
จะเรียกใช้บล็อกเมื่อรวบรวมและแสดงผลลัพธ์ของออบเจ็กต์ State
ที่อ่านในนั้น เมื่อออบเจ็กต์ State
รายการใดรายการหนึ่งซึ่งอ่านภายในบล็อก snapshotFlow
มีการกลายพันธุ์ ฟิวล์จะส่งค่าใหม่ไปยังตัวรวบรวมหากค่าใหม่ไม่เท่ากับค่าที่ส่งก่อนหน้านี้ (ลักษณะการทํางานนี้คล้ายกับของ Flow.distinctUntilChanged
)
ตัวอย่างต่อไปนี้แสดงผลข้างเคียงที่บันทึกเมื่อผู้ใช้เลื่อนผ่านรายการแรกในรายการไปยัง Analytics
val listState = rememberLazyListState()
LazyColumn(state = listState) {
// ...
}
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.map { index -> index > 0 }
.distinctUntilChanged()
.filter { it == true }
.collect {
MyAnalyticsService.sendScrolledPastFirstItemEvent()
}
}
ในโค้ดด้านบน listState.firstVisibleItemIndex
จะเปลี่ยนเป็นโฟลว์ที่รับประโยชน์จากความสามารถของโอเปอเรเตอร์ของโฟลว์
เอฟเฟกต์ที่เริ่มทำงานอีกครั้ง
เอฟเฟกต์บางอย่างในองค์ประกอบ เช่น LaunchedEffect
, produceState
หรือ DisposableEffect
จะใช้อาร์กิวเมนต์และคีย์จํานวนตัวแปร ซึ่งใช้เพื่อยกเลิกเอฟเฟกต์ที่ทำงานอยู่และเริ่มเอฟเฟกต์ใหม่ด้วยคีย์ใหม่
รูปแบบทั่วไปของ API เหล่านี้คือ
EffectName(restartIfThisKeyChanges, orThisKey, orThisKey, ...) { block }
เนื่องจากลักษณะการทํางานนี้มีความซับซ้อน ปัญหาอาจเกิดขึ้นได้หากพารามิเตอร์ที่ใช้เพื่อเริ่มเอฟเฟกต์อีกครั้งไม่ถูกต้อง
- การเริ่มเอฟเฟกต์ใหม่น้อยกว่าที่ควรจะเป็นอาจทำให้เกิดข้อบกพร่องในแอป
- การเริ่มเอฟเฟกต์ใหม่มากกว่าที่ควรจะเป็นอาจไม่มีประสิทธิภาพ
โดยทั่วไปแล้ว คุณควรเพิ่มตัวแปรแบบเปลี่ยนค่าได้และแบบเปลี่ยนค่าไม่ได้ที่ใช้ในบล็อกเอฟเฟกต์ของโค้ดเป็นพารามิเตอร์ไปยังคอมโพสิชันเอฟเฟกต์ นอกจากพารามิเตอร์ดังกล่าวแล้ว คุณยังเพิ่มพารามิเตอร์อื่นๆ เพื่อบังคับให้เอฟเฟกต์เริ่มทำงานอีกครั้งได้ หากการเปลี่ยนแปลงตัวแปรไม่ควรทําให้เอฟเฟกต์เริ่มทํางานอีกครั้ง คุณควรตัดตัวแปรนั้นใน rememberUpdatedState
หากตัวแปรไม่มีการเปลี่ยนแปลงเนื่องจากมีการรวมไว้ใน remember
ที่ไม่มีคีย์ คุณก็ไม่จําเป็นต้องส่งตัวแปรเป็นคีย์ให้กับเอฟเฟกต์
ในโค้ด DisposableEffect
ที่แสดงด้านบน เอฟเฟกต์จะใช้พารามิเตอร์เป็น lifecycleOwner
ที่ใช้ในบล็อก เนื่องจากการเปลี่ยนแปลงใดๆ กับพารามิเตอร์ดังกล่าวจะทำให้เอฟเฟกต์เริ่มทำงานอีกครั้ง
@Composable
fun HomeScreen(
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
onStart: () -> Unit, // Send the 'started' analytics event
onStop: () -> Unit // Send the 'stopped' analytics event
) {
// These values never change in Composition
val currentOnStart by rememberUpdatedState(onStart)
val currentOnStop by rememberUpdatedState(onStop)
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
/* ... */
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
คุณไม่จำเป็นต้องใช้ currentOnStart
และ currentOnStop
เป็นDisposableEffect
คีย์ เนื่องจากค่าของ currentOnStart
และ currentOnStop
จะไม่เปลี่ยนแปลงในองค์ประกอบเนื่องจากมีการใช้ rememberUpdatedState
หากคุณไม่ได้ส่ง lifecycleOwner
เป็นพารามิเตอร์และ lifecycleOwner
มีการเปลี่ยนแปลง HomeScreen
จะคอมโพสใหม่ แต่จะไม่ทิ้ง DisposableEffect
และเริ่มใหม่ ซึ่งจะทำให้เกิดปัญหาเนื่องจากมีการใช้ lifecycleOwner
ที่ไม่ถูกต้องนับจากจุดนั้นเป็นต้นไป
ค่าคงที่ใช้เป็นคีย์
คุณสามารถใช้ค่าคงที่ เช่น true
เป็นคีย์เอฟเฟกต์เพื่อให้เป็นไปตามวงจรชีวิตของเว็บไซต์ที่เรียกใช้ การใช้คำนี้ก็มีกรณีการใช้งานที่ถูกต้อง เช่น ตัวอย่าง LaunchedEffect
ที่แสดงด้านบน อย่างไรก็ตาม ก่อนดำเนินการดังกล่าว ให้คิดให้รอบคอบและตรวจสอบว่าคุณต้องการดำเนินการดังกล่าวจริงๆ
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- State และ Jetpack Compose
- Kotlin สำหรับ Jetpack Compose
- การใช้มุมมองในโหมดเขียน