Compose में बुरा असर

साइड इफ़ेक्ट का मतलब है, ऐप्लिकेशन की स्थिति में ऐसा बदलाव जो कंपोज़ेबल फ़ंक्शन के दायरे से बाहर होता है. कंपोज़ेबल के लाइफ़साइकल और अनचाहे तरीके से होने वाले रीकंपोज़िशन जैसी प्रॉपर्टी की वजह से, कंपोज़ेबल के रीकंपोज़िशन को अलग-अलग क्रम में लागू किया जा सकता है. साथ ही, ऐसे रीकंपोज़िशन भी हो सकते हैं जिन्हें खारिज किया जा सकता है. इसलिए, कंपोज़ेबल में साइड इफ़ेक्ट नहीं होने चाहिए.

हालांकि, कभी-कभी साइड इफ़ेक्ट ज़रूरी होते हैं. उदाहरण के लिए, स्नैकबार दिखाने या किसी खास स्थिति के हिसाब से दूसरी स्क्रीन पर नेविगेट करने जैसे एक बार होने वाले इवेंट को ट्रिगर करने के लिए. इन कार्रवाइयों को ऐसे कंट्रोल किए गए एनवायरमेंट से कॉल किया जाना चाहिए जिसे कंपोज़ेबल के लाइफ़साइकल के बारे में पता हो. इस पेज पर, आपको Jetpack Compose के साइड इफ़ेक्ट एपीआई के बारे में जानकारी मिलेगी.

स्टेट और इफ़ेक्ट के इस्तेमाल के उदाहरण

Compose में सोचने का तरीका दस्तावेज़ में बताया गया है कि कंपोज़ेबल में साइड इफ़ेक्ट नहीं होने चाहिए. अगर आपको ऐप्लिकेशन की स्थिति में बदलाव करने हैं, तो आपको इफ़ेक्ट एपीआई का इस्तेमाल करना चाहिए, ताकि साइड इफ़ेक्ट को अनुमान के मुताबिक लागू किया जा सके. इसके बारे में, स्टेट मैनेज करने के बारे में जानकारी देने वाले दस्तावेज़ में बताया गया है.

Compose में इफ़ेक्ट की अलग-अलग संभावनाएं होने की वजह से, इनका ज़रूरत से ज़्यादा इस्तेमाल किया जा सकता है. पक्का करें कि इनमें किया गया काम यूज़र इंटरफ़ेस (यूआई) से जुड़ा हो और यह एकतरफ़ा डेटा फ़्लो को न तोड़े. इसके बारे में, स्टेट मैनेज करने के बारे में जानकारी देने वाले दस्तावेज़ में बताया गया है.

LaunchedEffect: कंपोज़ेबल के दायरे में सस्पेंड फ़ंक्शन लागू करना

कंपोज़ेबल के लाइफ़साइकल के दौरान काम करने और सस्पेंड फ़ंक्शन को कॉल करने के लिए, LaunchedEffect कंपोज़ेबल का इस्तेमाल करें. जब LaunchedEffect कंपोज़िशन में शामिल होता है, तो यह पैरामीटर के तौर पर पास किए गए कोड के ब्लॉक के साथ एक कोरूटीन लॉन्च करता है. अगर 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 { 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 का इस्तेमाल करता है. इसके बाद, यह ऐल्फ़ा को क्रम से शून्य पर ऐनिमेट करता है और फिर से वापस लाता है, जिसमें animateToका इस्तेमाल किया जाता है. यह कंपोज़ेबल के लाइफ़साइकल के दौरान दोहराया जाएगा.

rememberCoroutineScope: कंपोज़ेबल के बाहर कोरूटीन लॉन्च करने के लिए, कंपोज़िशन की जानकारी वाला स्कोप पाना

LaunchedEffect एक कंपोज़ेबल फ़ंक्शन है. इसलिए, इसका इस्तेमाल सिर्फ़ अन्य कंपोज़ेबल फ़ंक्शन के अंदर किया जा सकता है. कंपोज़ेबल के बाहर कोरूटीन लॉन्च करने के लिए, हालांकि, इसका दायरा ऐसा होना चाहिए कि कंपोज़िशन से बाहर जाने पर यह अपने-आप रद्द हो जाए, इस्तेमाल करें rememberCoroutineScope. जब भी आपको एक या उससे ज़्यादा कोरूटीन के लाइफ़साइकल को मैन्युअल तरीके से कंट्रोल करना हो, तब भी rememberCoroutineScope का इस्तेमाल करें. उदाहरण के लिए, उपयोगकर्ता के किसी इवेंट के होने पर ऐनिमेशन रद्द करना.

rememberCoroutineScope एक कंपोज़ेबल फ़ंक्शन है, जो कंपोज़िशन के उस पॉइंट से बाउंड CoroutineScope को रिटर्न करता है जहां इसे कॉल किया जाता है. कॉल कंपोज़िशन से बाहर जाने पर, स्कोप रद्द हो जाएगा.

पिछले उदाहरण के मुताबिक, उपयोगकर्ता के Button पर टैप करने पर Snackbar दिखाने के लिए, इस कोड का इस्तेमाल किया जा सकता है:

@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 की कुंजियां बदलती हैं, तो कंपोज़ेबल को अपने मौजूदा इफ़ेक्ट को डिस्पोज़ (साफ़) करना होगा. साथ ही, इफ़ेक्ट को फिर से कॉल करके रीसेट करना होगा.

उदाहरण के लिए, Lifecycle इवेंट का इस्तेमाल करके, Analytics इवेंट भेजे जा सकते हैं LifecycleObserver. Compose में उन इवेंट को सुनने के लिए, ज़रूरत पड़ने पर ऑब्ज़र्वर को रजिस्टर और अनरजिस्टर करने के लिए, 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 */
}

ऊपर दिए गए कोड में, इफ़ेक्ट lifecycleOwner में observer जोड़ेगा. अगर lifecycleOwner बदलता है, तो इफ़ेक्ट डिस्पोज़ हो जाता है और नए lifecycleOwner के साथ रीस्टार्ट हो जाता है.

DisposableEffect में, कोड के ब्लॉक में आखिरी स्टेटमेंट के तौर पर onDispose क्लॉज़ शामिल होना चाहिए. ऐसा न होने पर, आईडीई, बिल्ड-टाइम की गड़बड़ी दिखाता है.

SideEffect: Compose स्टेट को नॉन-Compose कोड में पब्लिश करना

Compose स्टेट को कंपोज़ से मैनेज न किए जाने वाले ऑब्जेक्ट के साथ शेयर करने के लिए, SideEffect कंपोज़ेबल का इस्तेमाल करें. SideEffect का इस्तेमाल करने से यह पक्का होता है कि हर सफल रीकंपोज़िशन के बाद, इफ़ेक्ट लागू हो. दूसरी ओर, सफल रीकंपोज़िशन की गारंटी से पहले, इफ़ेक्ट लागू करना गलत है. ऐसा तब होता है, जब इफ़ेक्ट को सीधे कंपोज़ेबल में लिखा जाता है.

उदाहरण के लिए, आपकी 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 कंपोज़िशन के दायरे में एक कोरूटीन लॉन्च करता है, जो रिटर्न किए गए State में वैल्यू पुश कर सकता है. इसका इस्तेमाल, नॉन-Compose स्टेट को Compose स्टेट में बदलने के लिए करें. उदाहरण के लिए, Flow, LiveData या RxJava जैसे बाहरी सदस्यता-आधारित स्टेट को कंपोज़िशन में लाना.

produceState कंपोज़िशन में शामिल होने पर, प्रोड्यूसर लॉन्च होता है. कंपोज़िशन से बाहर जाने पर, यह रद्द हो जाएगा. रिटर्न किया गया State कंफ़्लैट होता है. एक ही वैल्यू सेट करने पर, रीकंपोज़िशन ट्रिगर नहीं होगा.

हालांकि, produceState एक कोरूटीन बनाता है, लेकिन इसका इस्तेमाल डेटा के नॉन-सस्पेंडिंग सोर्स को देखने के लिए भी किया जा सकता है. उस सोर्स की सदस्यता हटाने के लिए, the 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: एक या एक से ज़्यादा स्टेट ऑब्जेक्ट को दूसरे स्टेट में बदलना

Compose में, रीकंपोज़िशन तब होता है जब भी किसी देखे गए स्टेट ऑब्जेक्ट या कंपोज़ेबल इनपुट में बदलाव होता है. ऐसा हो सकता है कि स्टेट ऑब्जेक्ट या इनपुट, यूज़र इंटरफ़ेस (यूआई) को अपडेट करने की ज़रूरत से ज़्यादा बार बदल रहा हो. इससे गैर-ज़रूरी रीकंपोज़िशन हो सकता है.

आपको derivedStateOf फ़ंक्शन का इस्तेमाल तब करना चाहिए, जब कंपोज़ेबल के इनपुट, रीकंपोज़ करने की ज़रूरत से ज़्यादा बार बदल रहे हों. ऐसा अक्सर तब होता है, जब कोई चीज़ बार-बार बदल रही हो. जैसे, स्क्रोल की पोज़िशन. हालांकि, कंपोज़ेबल को सिर्फ़ तब प्रतिक्रिया देनी होती है, जब वह किसी तय थ्रेशोल्ड को पार कर जाए. derivedStateOf एक नया Compose स्टेट ऑब्जेक्ट बनाता है. इसे देखा जा सकता है और यह सिर्फ़ तब अपडेट होता है, जब इसकी ज़रूरत होती है. इस तरह, यह Kotlin Flows के ऑपरेटर की तरह काम करता है.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 स्टेट ऑब्जेक्ट को जोड़ा जाता है, तो 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> का इस्तेमाल करें. 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 को फ़्लो में बदला जाता है. इससे फ़्लो के ऑपरेटर की सुविधाओं का फ़ायदा मिल सकता है.

इफ़ेक्ट रीस्टार्ट करना

Compose में कुछ इफ़ेक्ट, जैसे कि LaunchedEffect, produceState या DisposableEffect, वैरिएबल की संख्या में आर्ग्युमेंट, कुंजियां लेते हैं. इनका इस्तेमाल, चालू इफ़ेक्ट को रद्द करने और नई कुंजियों के साथ नया इफ़ेक्ट शुरू करने के लिए किया जाता है.

इन एपीआई का सामान्य फ़ॉर्म यह है:

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 का उदाहरण. हालांकि, ऐसा करने से पहले, दो बार सोचें और पक्का करें कि आपको इसकी ज़रूरत है.