ผลสะท้อน คือการเปลี่ยนแปลงสถานะของแอปที่เกิดขึ้นนอกขอบเขตของฟังก์ชันที่ประกอบกันได้ เนื่องจากวงจรการทำงานและพร็อพเพอร์ตี้ของฟังก์ชันที่ประกอบกันได้ เช่น การประกอบใหม่ที่คาดเดาไม่ได้ การประกอบใหม่ของฟังก์ชันที่ประกอบกันได้ในลำดับที่ต่างกัน หรือ การประกอบใหม่ที่อาจถูกยกเลิก ฟังก์ชันที่ประกอบกันได้ จึงควรไม่มีผลสะท้อน
อย่างไรก็ตาม บางครั้งก็จำเป็นต้องมีผลสะท้อน เช่น เพื่อทริกเกอร์เหตุการณ์แบบครั้งเดียว เช่น การแสดง Snackbar หรือการไปยังหน้าจออื่นตามเงื่อนไขสถานะที่กำหนด ควรเรียกใช้การดำเนินการเหล่านี้จากสภาพแวดล้อมที่มีการควบคุมซึ่งทราบวงจรการทำงานของฟังก์ชันที่ประกอบกันได้ ในหน้านี้ คุณจะได้เรียนรู้เกี่ยวกับ API ผลสะท้อนต่างๆ ที่ Jetpack Compose มีให้
กรณีการใช้งานสถานะและผลกระทบ
ตามที่กล่าวไว้ในเอกสารประกอบเรื่องการคิดแบบ Compose ฟังก์ชันที่ประกอบกันได้ควรไม่มีผลสะท้อน เมื่อต้องการทำการเปลี่ยนแปลงสถานะของแอป (ตามที่อธิบายไว้ในเอกสารประกอบเรื่องการจัดการสถานะ) คุณควรใช้ Effect API เพื่อให้ผลสะท้อนเหล่านั้นทำงานในลักษณะที่คาดการณ์ได้
เนื่องจากเอฟเฟกต์เปิดโอกาสต่างๆ ใน Compose จึงอาจมีการใช้งานมากเกินไปได้ง่าย ตรวจสอบว่างานที่คุณทำในเอฟเฟกต์นั้นเกี่ยวข้องกับ UI และไม่ละเมิด โฟลว์ข้อมูลแบบทิศทางเดียว ตามที่อธิบายไว้ใน เอกสารประกอบเรื่องการจัดการสถานะ
LaunchedEffect: เรียกใช้ฟังก์ชันระงับในขอบเขตของฟังก์ชันที่ประกอบกันได้
หากต้องการทำงานตลอดอายุการใช้งานของฟังก์ชันที่ประกอบกันได้และสามารถเรียกใช้
ฟังก์ชันระงับได้ ให้ใช้
LaunchedEffect
ฟังก์ชันที่ประกอบกันได้ เมื่อ LaunchedEffect เข้าสู่การประกอบ ระบบจะเปิดใช้ Coroutine ด้วยบล็อกโค้ดที่ส่งผ่านเป็นพารามิเตอร์ 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 { mutableLongStateOf(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 และกลับมาอีกครั้งตามลำดับโดยใช้
animateTo
การดำเนินการนี้จะทำซ้ำตลอดอายุการใช้งานของฟังก์ชันที่ประกอบกันได้
rememberCoroutineScope: รับขอบเขตที่ทราบการประกอบเพื่อเปิดใช้ Coroutine นอกฟังก์ชันที่ประกอบกันได้
เนื่องจาก 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 มีค่าล่าสุดที่ LandingScreen ประกอบใหม่ เสมอ คุณต้องครอบแลมดา onTimeout ด้วยฟังก์ชัน rememberUpdatedState
ควรใช้ State ที่แสดงผล currentOnTimeout ในโค้ดในเอฟเฟกต์
DisposableEffect: เอฟเฟกต์ที่ต้องมีการล้างข้อมูล
สำหรับผลสะท้อนที่ต้องมีการ ล้างข้อมูล หลังจากคีย์มีการเปลี่ยนแปลงหรือหากฟังก์ชันที่ประกอบกันได้ออกจากฟังก์ชันที่ประกอบกันได้ ให้ใช้
DisposableEffect
หากคีย์ DisposableEffect มีการเปลี่ยนแปลง ฟังก์ชันที่ประกอบกันได้จะต้อง ยกเลิก (ล้างข้อมูล) เอฟเฟกต์ปัจจุบัน และรีเซ็ตโดยการเรียกใช้เอฟเฟกต์อีกครั้ง
ตัวอย่างเช่น คุณอาจต้องการส่งเหตุการณ์ Analytics ตามเหตุการณ์
Lifecycleโดยใช้
LifecycleObserver
หากต้องการฟังเหตุการณ์เหล่านั้นใน Compose ให้ใช้ DisposableEffect เพื่อลงทะเบียนและยกเลิกการลงทะเบียน Observer เมื่อจำเป็น
@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: เผยแพร่สถานะ Compose ไปยังโค้ดที่ไม่ใช่ Compose
หากต้องการแชร์สถานะ Compose กับออบเจ็กต์ที่ไม่ได้จัดการโดย Compose ให้ใช้
SideEffect
ฟังก์ชันที่ประกอบกันได้ การใช้ SideEffect จะรับประกันว่าเอฟเฟกต์จะทำงานหลังจากการจัดองค์ประกอบใหม่ทุกครั้งที่สำเร็จ ในทางกลับกัน การดำเนินการเอฟเฟกต์ก่อนที่จะรับประกันการจัดองค์ประกอบใหม่ที่สำเร็จนั้นไม่ถูกต้อง ซึ่งเป็นกรณีที่เขียนเอฟเฟกต์โดยตรงใน Composable
ตัวอย่างเช่น ไลบรารี Analytics อาจอนุญาตให้คุณแบ่งกลุ่มฐานผู้ใช้โดยแนบข้อมูลเมตาที่กำหนดเอง ("พร็อพเพอร์ตี้ผู้ใช้" ในตัวอย่างนี้) กับเหตุการณ์ Analytics ทั้งหมดที่ตามมา หากต้องการสื่อสารประเภทผู้ใช้ของผู้ใช้ปัจจุบันไปยังไลบรารี Analytics ให้ใช้ 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: แปลงสถานะที่ไม่ใช่ Compose เป็นสถานะ Compose
produceState
จะเปิดใช้ Coroutine ที่กำหนดขอบเขตไว้ที่ฟังก์ชันที่ประกอบกันได้ ซึ่งสามารถพุชค่าลงใน
Stateที่แสดงผล ใช้ฟังก์ชันนี้เพื่อแปลงสถานะที่ไม่ใช่ Compose เป็นสถานะ Compose เช่น การนำสถานะที่ขับเคลื่อนด้วยการสมัครใช้บริการภายนอก เช่น Flow, LiveData หรือ RxJava เข้าสู่ฟังก์ชันที่ประกอบกันได้
ระบบจะเปิดใช้ Producer เมื่อ produceState เข้าสู่ฟังก์ชันที่ประกอบกันได้ และจะยกเลิกเมื่อออกจากฟังก์ชันที่ประกอบกันได้ 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 รายการเป็นสถานะอื่น
ใน Compose การจัดองค์ประกอบใหม่ จะเกิดขึ้น ทุกครั้งที่ออบเจ็กต์สถานะที่สังเกตได้หรืออินพุตของ Composable มีการเปลี่ยนแปลง ออบเจ็กต์สถานะหรืออินพุตอาจมีการเปลี่ยนแปลงบ่อยกว่าที่ UI จำเป็นต้องอัปเดตจริง ซึ่งนำไปสู่การจัดองค์ประกอบใหม่ที่ไม่จำเป็น
คุณควรใช้ฟังก์ชัน derivedStateOf
เมื่ออินพุตของฟังก์ชันที่ประกอบกันได้มีการเปลี่ยนแปลงบ่อยกว่าที่คุณต้องการ
ประกอบใหม่ กรณีนี้มักเกิดขึ้นเมื่อมีบางอย่างเปลี่ยนแปลงบ่อย เช่น ตำแหน่งการเลื่อน แต่ฟังก์ชันที่ประกอบกันได้จำเป็นต้องตอบสนองต่อการเปลี่ยนแปลงนั้นก็ต่อเมื่อค่าข้ามเกณฑ์ที่กำหนด derivedStateOf จะสร้างออบเจ็กต์สถานะ Compose ใหม่ที่คุณสังเกตได้ ซึ่งจะอัปเดตเฉพาะเมื่อจำเป็นเท่านั้น ด้วยวิธีนี้ ฟังก์ชันนี้จะทำงาน
คล้ายกับ Kotlin Flow
distinctUntilChanged()
โอเปอเรเตอร์
การใช้งานที่ถูกต้อง
ข้อมูลโค้ดต่อไปนี้แสดงกรณีการใช้งานที่เหมาะสมสำหรับ 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
ในข้อมูลโค้ดนี้ fullName ต้องอัปเดตบ่อยเท่ากับ firstName และ lastName ดังนั้นจึงไม่มีการจัดองค์ประกอบใหม่มากเกินไป และไม่จำเป็นต้องใช้ derivedStateOf
snapshotFlow: แปลงสถานะของ Compose เป็น Flow
ใช้ snapshotFlow
เพื่อแปลง State<T>
ออบเจ็กต์เป็น Flow แบบเย็น snapshotFlow จะเรียกใช้บล็อกเมื่อมีการรวบรวมและปล่อยผลลัพธ์ของออบเจ็กต์ State ที่อ่านในบล็อก เมื่อออบเจ็กต์ State รายการใดรายการหนึ่งที่อ่านภายในบล็อก snapshotFlow มีการเปลี่ยนแปลง Flow จะปล่อยค่าใหม่ไปยังตัวรวบรวมหากค่าใหม่ไม่ เท่ากับค่าที่ปล่อยก่อนหน้า (ลักษณะการทำงานนี้คล้ายกับ 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 จะถูกแปลงเป็น Flow ที่ได้รับประโยชน์จากพลังของโอเปอเรเตอร์ของ Flow
การรีสตาร์ทเอฟเฟกต์
เอฟเฟกต์บางอย่างใน Compose เช่น 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 เนื่องจากค่าของคีย์เหล่านี้ไม่เปลี่ยนแปลงในฟังก์ชันที่ประกอบกันได้เนื่องจากการใช้ rememberUpdatedState หากคุณไม่ส่ง lifecycleOwner เป็นพารามิเตอร์และพารามิเตอร์มีการเปลี่ยนแปลง HomeScreen จะประกอบใหม่ แต่ DisposableEffect จะไม่ถูกยกเลิกและรีสตาร์ท ซึ่งทำให้เกิดปัญหาเนื่องจากมีการใช้ lifecycleOwner ที่ไม่ถูกต้องนับจากจุดนั้นเป็นต้นไป
ค่าคงที่เป็นคีย์
คุณสามารถใช้ค่าคงที่ เช่น true เป็นคีย์เอฟเฟกต์เพื่อให้เอฟเฟกต์เป็นไปตามวงจรการทำงานของตำแหน่งที่เรียกใช้ มีกรณีการใช้งานที่ถูกต้องสำหรับฟังก์ชันนี้ เช่น ตัวอย่าง LaunchedEffect ที่แสดงด้านบน อย่างไรก็ตาม ก่อนที่จะดำเนินการดังกล่าว โปรดคิดให้รอบคอบและตรวจสอบว่าคุณต้องการใช้ฟังก์ชันนี้จริงๆ
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- สถานะและ Jetpack Compose
- Kotlin สำหรับ Jetpack Compose
- การใช้ View ใน Compose