কম্পোজ এর পার্শ্বপ্রতিক্রিয়া

সাইড-ইফেক্ট হলো অ্যাপের অবস্থার এমন একটি পরিবর্তন যা একটি কম্পোজেবল ফাংশনের আওতার বাইরে ঘটে। কম্পোজেবলগুলোর লাইফসাইকেল এবং কিছু বৈশিষ্ট্যের কারণে, যেমন—অপ্রত্যাশিত রিকম্পোজিশন, বিভিন্ন ক্রমে কম্পোজেবলগুলোর রিকম্পোজিশন সম্পাদন করা, বা বাতিলযোগ্য রিকম্পোজিশন, কম্পোজেবলগুলোকে আদর্শগতভাবে সাইড-ইফেক্ট মুক্ত হওয়া উচিত

তবে, কখনও কখনও সাইড-ইফেক্টের প্রয়োজন হয়, যেমন—একটি স্নাকবার দেখানো বা কোনো নির্দিষ্ট স্টেট কন্ডিশনের ভিত্তিতে অন্য স্ক্রিনে নেভিগেট করার মতো কোনো ওয়ান-অফ ইভেন্ট ট্রিগার করার জন্য। এই অ্যাকশনগুলোকে এমন একটি নিয়ন্ত্রিত পরিবেশ থেকে কল করা উচিত, যা কম্পোজেবলটির লাইফসাইকেল সম্পর্কে অবগত থাকে। এই পৃষ্ঠায়, আপনি জেটপ্যাক কম্পোজের দেওয়া বিভিন্ন সাইড-ইফেক্ট এপিআই সম্পর্কে জানতে পারবেন।

অবস্থা এবং প্রভাব ব্যবহারের ক্ষেত্র

"থিংকিং ইন কম্পোজ" ডকুমেন্টেশনে যেমন বলা হয়েছে, কম্পোজেবলগুলো সাইড-ইফেক্ট মুক্ত হওয়া উচিত। যখন আপনার অ্যাপের স্টেটে পরিবর্তন আনার প্রয়োজন হয় (যেমনটি "ম্যানেজিং স্টেট" ডকুমেন্টেশনে বর্ণনা করা হয়েছে), তখন আপনার ইফেক্ট এপিআই (Effect API) ব্যবহার করা উচিত, যাতে সেই সাইড-ইফেক্টগুলো একটি অনুমানযোগ্য পদ্ধতিতে সম্পাদিত হয়

কম্পোজে ইফেক্টগুলো বিভিন্ন সম্ভাবনার দ্বার উন্মোচন করার কারণে, এগুলোর ব্যবহার সহজেই অতিরিক্ত হয়ে যেতে পারে। নিশ্চিত করুন যে আপনি এগুলোর মাধ্যমে যে কাজ করছেন তা UI-সম্পর্কিত এবং 'ম্যানেজিং স্টেট' ডকুমেন্টেশনে ব্যাখ্যা করা অনুযায়ী একমুখী ডেটা প্রবাহকে ব্যাহত করে না।

LaunchedEffect : একটি কম্পোজেবল-এর স্কোপের মধ্যে সাসপেন্ড ফাংশনগুলো চালায়।

একটি কম্পোজেবলের জীবনচক্র জুড়ে কাজ সম্পাদন করতে এবং সাসপেন্ড ফাংশন কল করার ক্ষমতা পেতে, LaunchedEffect কম্পোজেবলটি ব্যবহার করুন। যখন LaunchedEffect কম্পোজিশনে প্রবেশ করে, তখন এটি প্যারামিটার হিসেবে পাস করা কোড ব্লকসহ একটি কো-রুটিন চালু করে। LaunchedEffect কম্পোজিশন থেকে বেরিয়ে গেলে কো-রুটিনটি বাতিল হয়ে যাবে। যদি LaunchedEffect LaunchedEffect ভিন্ন কী (key) দিয়ে পুনরায় কম্পোজ করা হয় (নিচের ' Restarting Effects' অংশটি দেখুন), তাহলে বিদ্যমান কো-রুটিনটি বাতিল হয়ে যাবে এবং নতুন সাসপেন্ড ফাংশনটি একটি নতুন কো-রুটিনে চালু হবে।

উদাহরণস্বরূপ, এখানে একটি অ্যানিমেশন রয়েছে যা একটি কনফিগারযোগ্য বিলম্ব সহ আলফা মানকে স্পন্দিত করে:

// 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 : যে প্রভাবগুলো পরিষ্কার করা প্রয়োজন

যেসব সাইড এফেক্টের কী (key) পরিবর্তনের পর বা কম্পোজেবলটি কম্পোজিশন (Composition) থেকে বেরিয়ে গেলে পরিষ্কার করার প্রয়োজন হয়, সেগুলোর জন্য DisposableEffect ) ব্যবহার করুন। যদি DisposableEffect কীগুলো পরিবর্তিত হয়, তাহলে কম্পোজেবলটিকে তার বর্তমান এফেক্টটি ডিসপোজ (পরিষ্কার করার কাজটি) করতে হবে এবং এফেক্টটিকে পুনরায় কল করে রিসেট করতে হবে।

উদাহরণস্বরূপ, আপনি একটি LifecycleObserver ব্যবহার করে Lifecycle ইভেন্টের উপর ভিত্তি করে অ্যানালিটিক্স ইভেন্ট পাঠাতে চাইতে পারেন। 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 */
}

উপরের কোডে, ইফেক্টটি lifecycleOwnerobserver যুক্ত করবে। যদি 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 কম্পোজিশনের স্কোপের মধ্যে একটি কো-রুটিন চালু করে, যা রিটার্ন করা State -এ ভ্যালু পুশ করতে পারে। নন-কম্পোজ স্টেটকে কম্পোজ স্টেটে রূপান্তর করতে এটি ব্যবহার করুন; উদাহরণস্বরূপ, Flow , LiveData বা RxJava মতো এক্সটার্নাল সাবস্ক্রিপশন-চালিত স্টেটকে কম্পোজিশনের মধ্যে নিয়ে আসা।

produceState যখন Composition-এ প্রবেশ করে তখন প্রডিউসারটি চালু হয়, এবং Composition থেকে বেরিয়ে গেলে এটি বাতিল হয়ে যাবে। ফেরত আসা 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 : এক বা একাধিক স্টেট অবজেক্টকে অন্য স্টেটে রূপান্তর করুন

Compose-এ, যখনই কোনো পর্যবেক্ষণাধীন স্টেট অবজেক্ট বা কম্পোজেবল ইনপুট পরিবর্তিত হয়, তখনই রিকম্পোজিশন ঘটে। একটি স্টেট অবজেক্ট বা ইনপুট UI-এর প্রকৃত আপডেটের চেয়ে বেশি ঘন ঘন পরিবর্তিত হতে পারে, যার ফলে অপ্রয়োজনীয় রিকম্পোজিশন ঘটে।

যখন আপনার কম্পোজেবলের ইনপুটগুলো পুনরায় কম্পোজ করার প্রয়োজনের চেয়ে বেশি ঘন ঘন পরিবর্তিত হয়, তখন আপনার 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-এর State-কে Flow-তে রূপান্তর করুন

State<T> অবজেক্টগুলোকে একটি কোল্ড ফ্লো-তে রূপান্তর করতে snapshotFlow ব্যবহার করুন। ডেটা সংগ্রহ করা হলে snapshotFlow তার নিজস্ব ব্লকটি চালায় এবং এর মধ্যে পঠিত State অবজেক্টগুলোর ফলাফল ইমিট করে। যখন snapshotFlow ব্লকের ভিতরে পঠিত কোনো State অবজেক্ট পরিবর্তিত হয়, তখন যদি নতুন মানটি পূর্ববর্তী ইমিট করা মানের সমান না হয়, তাহলে ফ্লো-টি তার কালেক্টরের কাছে নতুন মানটি ইমিট করবে (এই আচরণটি Flow.distinctUntilChanged এর অনুরূপ)।

নিম্নলিখিত উদাহরণটি এমন একটি সাইড এফেক্ট দেখায় যা ব্যবহারকারী যখন কোনো তালিকার প্রথম আইটেমটি স্ক্রল করে পার হয়ে যায়, তখন তা অ্যানালিটিক্সে রেকর্ড করে:

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 , পরিবর্তনশীল সংখ্যক আর্গুমেন্ট বা 'কী' গ্রহণ করে, যা চলমান এফেক্টটি বাতিল করতে এবং নতুন কী-গুলো ব্যবহার করে একটি নতুন এফেক্ট শুরু করতে ব্যবহৃত হয়।

এই এপিআইগুলোর সাধারণ রূপটি হলো:

EffectName(restartIfThisKeyChanges, orThisKey, orThisKey, ...) { block }

এই আচরণের সূক্ষ্মতার কারণে, ইফেক্টটি পুনরায় চালু করার জন্য ব্যবহৃত প্যারামিটারগুলো সঠিক না হলে সমস্যা দেখা দিতে পারে:

  • প্রয়োজনের চেয়ে কম রিস্টার্ট করলে আপনার অ্যাপে বাগ দেখা দিতে পারে।
  • প্রয়োজনের চেয়ে বেশি রিস্টার্ট করা অকার্যকর হতে পারে।

সাধারণত, ইফেক্ট কোড ব্লকে ব্যবহৃত পরিবর্তনযোগ্য (mutable) এবং অপরিবর্তনযোগ্য (immutable) ভেরিয়েবলগুলোকে ইফেক্ট কম্পোজেবলের প্যারামিটার হিসেবে যোগ করা উচিত। এগুলো ছাড়াও, ইফেক্টটিকে জোর করে পুনরায় চালু করার জন্য আরও প্যারামিটার যোগ করা যেতে পারে। যদি কোনো ভেরিয়েবলের পরিবর্তনের কারণে ইফেক্টটি পুনরায় চালু না হয়, তবে ভেরিয়েবলটিকে rememberUpdatedState এর মধ্যে রাখতে হবে। যদি ভেরিয়েবলটি কোনো কী (key) ছাড়া remember মধ্যে থাকার কারণে কখনও পরিবর্তিত না হয়, তবে ভেরিয়েবলটিকে ইফেক্টে কী (key) হিসেবে পাস করার প্রয়োজন নেই।

উপরে দেখানো 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 ব্যবহারের ফলে Composition-এ এদের মান কখনও পরিবর্তিত হয় না। যদি আপনি lifecycleOwner প্যারামিটার হিসেবে পাস না করেন এবং এর মান পরিবর্তিত হয়, তাহলে HomeScreen পুনরায় কম্পোজ করে, কিন্তু DisposableEffect টি ডিসপোজ হয়ে পুনরায় চালু হয় না। এর ফলে সমস্যা তৈরি হয়, কারণ সেই মুহূর্ত থেকে ভুল lifecycleOwner ব্যবহৃত হতে থাকে।

ধ্রুবকগুলি চাবি হিসাবে

কল সাইটের লাইফসাইকেল অনুসরণ করানোর জন্য আপনি ইফেক্ট কী হিসেবে true মতো একটি কনস্ট্যান্ট ব্যবহার করতে পারেন। এর কিছু বৈধ ব্যবহারক্ষেত্রও রয়েছে, যেমন উপরে দেখানো LaunchedEffect উদাহরণটি। তবে, তা করার আগে, দুবার ভাবুন এবং নিশ্চিত হয়ে নিন যে আপনার সত্যিই এটাই প্রয়োজন।

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}