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