SideEffect Jetpack Compose-এ একটি composable ফাংশন যা প্রতিটি সফল পুনর্বিন্যাসে পাস করা কোড ব্লক চালায়। LaunchedEffect এবং DisposableEffect-এর বিপরীতে, SideEffect কী থেকে আবদ্ধ নয় এবং এর কোনো পরিষ্কার ব্লক নেই — এটি শুধু প্রতিটি রেন্ডারিংয়ের পর Compose অবস্থাকে বাহ্যিক সিস্টেমের সাথে সিঙ্ক্রোনাইজ করে। এটি কলব্যাক ফাংশন আপডেট করা, ViewPager-এর সাথে সিঙ্ক্রোনাইজেশন এবং Analytics SDK-এ ডেটা পাস করার জন্য আদর্শ করে তোলে। Android Developers Documentation (2025) অনুযায়ী, Compose সফল পুনর্বিন্যাস নিশ্চিত করার পরই SideEffect চালু হয়, এবং পুনর্বিন্যাস বাদ দেওয়া হলে এটি চালু হয় না।
মূল বিষয়
SideEffect Jetpack Compose-এ সবচেয়ে সহজ সাইড-ইফেক্ট API। এটি একটি composable উপাদানের প্রতিটি সফল পুনর্বিন্যাসে একটি কোড ব্লক চালায়। এখানে "সফল" শব্দটি গুরুত্বপূর্ণ: যদি Compose সিদ্ধান্ত নয় যে পুনর্বিন্যাস প্রয়োজন নয় (উদাহরণস্বরূপ, সকল ইনপুট প্যারামিটার অপরিবর্তিত এবং ফলাফল একই হবে), তবে SideEffect চালু হয় না। এটি নিশ্চিত করে যে সিঙ্ক্রোনাইজেশন ব্লক কেবল তখনই ডাকা হয় যখন UI প্রকৃতেই পরিবর্তিত হয়েছে।
SideEffect-এর প্রধান ব্যবহার Compose অবস্থাকে সেই উপাদানগুলির সাথে সিঙ্ক্রোনাইজ করা যা Compose ট্রির অংশ নয়। সাধারণ উদাহরণের মধ্যে রয়েছে: Legacy View সিস্টেমে কলব্যাক ফাংশন আপডেট করা, ViewPager-এ বর্তমান অবস্থা পাস করা, প্রদর্শিত ডেটা পরিবর্তনে Analytics SDK-এ একটি ইভেন্ট পাঠানো, এবং ম্যাপিং SDK-এর সাথে সিঙ্ক্রোনাইজ করা যা বাহ্যিক ফরম্যাটে আপডেট আশা করে।
Android Developer Blog (2025) অনুযায়ী, SideEffect প্রায়ই remember-এর সাথে ব্যবহৃত হয়: remember একটি উপাদান (যেমন একটি কলব্যাক) সংরক্ষণ করে, এবং SideEffect যখনই কোনো নির্ভরতা পরিবর্তিত হয় এটি আপডেট করে। এই প্যাটার্ন বিশেষ করে সেই লাইব্রেরিগুলির জন্য গুরুত্বপূর্ণ যা লিসনার অবজেক্ট গ্রহণ করে এবং আপডেটে সেগুলিকে পুনরায় তৈরি করে না — SideEffect ছাড়া, লিসনারটিতে বর্তমান অবস্থার একটি পুরানো রিফারেন্স থাকবে।
@Composable
fun MapScreen(zoomLevel: Int, markers: List<Marker>) {
val mapView = remember { MapView(LocalContext.current) }
SideEffect {
mapView.setZoom(zoomLevel)
mapView.updateMarkers(markers)
}
AndroidView(factory = { mapView })
}
SideEffect বোঝার জন্য, Jetpack Compose-এর নির্বাহন দর্শামূহ বোঝা প্রয়োজন। Compose প্রতিটি ফ্রেমের জন্য তিনটি দর্শার মধ্য দিয়ে যায়: Composition (কী প্রদর্শন করবে), Layout (কোথায় প্রদর্শন করবে), Drawing (কীভাবে প্রদর্শন করবে)। SideEffect Composition দর্শার শেষে চালু হয় — সকল composable ফাংশন চলার পর, কিন্তু Layout দর্শার আগে। এটি নিশ্চিত করে যে SideEffect পুনর্বিন্যাসের পর সব ভেরিয়েবলের শেষ অবস্থা দেখে।
জীবনচক্রে এই অবস্থান একটি গুরুত্বপূর্ণ সুবিধা দেয়: SideEffect অসীম পুনর্বিন্যাস ঘটাতে পারে না, যদিও এর মধ্যে অবস্থা পরিবর্তিত হয়। যেহেতু এটি কম্পোজিশনের পর চালু হয়, SideEffect-এর মধ্যে করা পরিবর্তনগুলি কেবল পরবর্তী ফ্রেমে প্রয়োগ হবে — এটি একটি composable ফাংশনের মধ্যে অবস্থা পরিবর্তনের চক্রগুলি প্রতিরোধ করে (যখন কম্পোজিশনের মধ্যে setState বর্তমান কম্পোজিশন শেষ হওয়ার আগে একটি নতুন কম্পোজিশন ট্রিগার করে)।
আরও একটি বৈশিষ্ট্য হল SideEffect কী দ্বারা অপ্টিমাইজ হয় না। এটি যে নির্দিষ্ট অবস্থা পরিবর্তিত হোক না কেন, প্রতিটি পুনর্বিন্যাসে চালু হয়। যদি আপনার আরও সঠিক নিয়ন্ত্রণের প্রয়োজন হয় (কেবল তখন চালু করুন যখন একটি নির্দিষ্ট প্যারামিটার পরিবর্তিত হয়), তবে কী সহিত LaunchedEffect ব্যবহার করুন বা remember-এর মাধ্যমে SideEffect-কে পরিবর্তন যাচাইয়ে মোড়ে দিন।
// SideEffect optimized with remember
var currentZoom by remember { mutableIntStateOf(zoomLevel) }
SideEffect {
if (currentZoom != zoomLevel) {
map.animateToZoom(zoomLevel)
currentZoom = zoomLevel
}
}
// Without this check SideEffect would call animateToZoom
// on every recomposition, even if zoomLevel didn't change
SideEffect-এর জন্য সবচেয়ে সাধারণ ব্যবহারিক পরিদর্শন হল বর্তমান অবস্থা ক্যাপচার করে থাকে কলব্যাক ফাংশন আপডেট করা। Jetpack Compose-এ একে "callback lifecycle management" বলা হয়। সমস্যা হল Kotlin-এ lambda এক্সপ্রেশন রিফারেন্সের মাধ্যমে ভেরিয়েবল ক্যাপচার করে, এবং যদি একটি কলব্যাক একটি মান দিয়ে তৈরি করা হয় এবং পরে ভেরিয়েবল পরিবর্তিত হয় — কলব্যাক পুরানো মান ব্যবহার করতে থাকে।
একটি উদাহরণ দেখুন: Android-এর জন্য Google Maps SDK setOnCameraMoveListener()-এর মাধ্যমে একটি OnCameraMoveListener অবজেক্ট গ্রহণ করে। যদি আপনি একটি lambda পাস করেন যা isTrackingEnabled ক্যাপচার করে, তবে isTrackingEnabled পরিবর্তিত হলে lambda আপডেট হবে না — Maps SDK পুরানো ডেটা সহ পুরানো কলব্যাক ডাকতে থাকবে। SideEffect এই সমস্যা সমাধান করে: এটি প্রতিটি পুনর্বিন্যাসে লিসনারটিকে পুনরায় নির্ধারণ করে, যা নিশ্চিত করে যে SDK সর্বশেষ অবস্থা সহ সক্রিয় lambda ব্যবহার করে।
Maps SDK for Android Documentation (2025) অনুযায়ী, Google Maps-কে Jetpack Compose-এর সাথে সংযুক্ত করার সময় Google ঠিক এই প্যাটার্ন সুপারিশ করে। অনুরূপ পদ্ধতি WebView, VideoView, TextureView এবং অন্যান্য View-ভিত্তিক উপাদানের জন্য ব্যবহৃত হয় যা set-পদ্ধতির মাধ্যমে callbacks গ্রহণ করে। SideEffect নিশ্চিত করে যে প্রতিটি অবস্থা পরিবর্তনের সাথে callbacks হালনাগাদ থাকে।
@Composable
fun MapComposable(isTrackingEnabled: Boolean, onMarkerClick: (Marker) -> Unit) {
val mapView = remember { MapView(LocalContext.current) }
SideEffect {
mapView.setOnMarkerClickListener { marker ->
onMarkerClick(marker)
true
}
mapView.isTrafficEnabled = isTrackingEnabled
}
AndroidView(factory = { mapView })
}
SideEffect-এর আরও একটি গুরুত্বপূর্ণ পরিদর্শন হল UI অবস্থা পরিবর্তনে বিশ্লেষণ ব্যবস্থায় ইভেন্ট পাঠানো। উদাহরণস্বরূপ, যখন একজন ব্যবহারকারী Compose স্ক্রিনের মধ্যে TabLayout-এ ট্যাব পরিবর্তন করে, SideEffect বর্তমান নির্বাচিত ট্যাব অবস্থা Firebase Analytics বা AppsFlyer-এ পাঠাতে পারে। যতবার নির্বাচিত ট্যাব পরিবর্তিত হয় (এবং পুনর্বিন্যাস হয়), SideEffect সংশ্লিষ্ট ইভেন্ট পাঠায়।
onClick বা onTabSelected-এ সরাসরি ইভেন্ট পাঠানোর পার্থক্য হল SideEffect যে কোনো উৎস থেকে অবস্থা পরিবর্তনে ট্রিগার হয় — কেবল ব্যবহারকারী কর্মকাণ্ডের কারণেই নয়, বরং প্রোগ্রামেটিক পরিবর্তন, স্ক্রিন ঘূর্ণনের পর অবস্থা পুনরুদ্ধার বা Deep Links-এর কারণেও। এটি SideEffect-কে পরিবর্তন উৎস থেকে স্বতন্ত্র একটি সার্বভৌমিক সিঙ্ক্রোনাইজেশন প্রক্রিয়ায় পরিণত করে।
Firebase Best Practices (Google, 2025) অনুযায়ী, SideEffect-এর মাধ্যমে বিশ্লেষণ ইভেন্ট পাঠানো ব্যবহারকারীর যাত্রার আরও সম্পূর্ণ চিত্র দেয়, কারণ এটি অবস্থার সকল পরিবর্তন ক্যাপচার করে, যার মধ্যে সেগুলিও অন্তর্ভুক্ত যা সরাসরি ব্যবহারকারী কর্ম ছাড়াই হয়। তবে এটি বেশি করা উচিত নয়: প্রতিটি বিশ্লেষণ ইভেন্ট একটি নেটওয়ার্ক অনুরোধ, তাই বারবার পরিবর্তনশীল অবস্থা (স্ক্রল অবস্থান, আঙ্গুলের স্থানাঙ্ক) এর জন্য SideEffect উপযুক্ত নয় — debounce ব্যবহার করুন বা কেবল গুরুত্বপূর্ণ পরিবর্তনে ইভেন্ট পাঠান।
@Composable
fun ProductScreen(selectedTab: ProductTab, productId: String) {
val firebaseAnalytics = remember { FirebaseAnalytics.getInstance(LocalContext.current) }
SideEffect {
val params = Bundle().apply {
putString(FirebaseAnalytics.Param.CONTENT_TYPE, selectedTab.name)
putString(FirebaseAnalytics.Param.ITEM_ID, productId)
}
firebaseAnalytics.logEvent( FirebaseAnalytics.Event.VIEW_ITEM, params)
}
// UI with TabRow and selected tab
}
SideEffect এবং LaunchedEffect-এর মধ্যে পছন্দ দুটি কারণের উপর নির্ভর করে: অসিঙ্ক্রোনাস নির্বাহন প্রয়োজন কিনা এবং কী-ভিত্তিক নিয়ন্ত্রণ প্রয়োজন কিনা। SideEffect সিঙ্ক্রোনাস এবং প্রতিটি পুনর্বিন্যাসে চালু হয়। LaunchedEffect অসিঙ্ক্রোনাস (কোরুটিন) এবং কেবল তখন চালু হয় যখন কী পরিবর্তিত হয়, প্রতিটি পুনর্বিন্যাসে নয়।
| বৈশিষ্ট্য | SideEffect | LaunchedEffect |
|---|---|---|
| নির্বাহন | প্রতিটি পুনর্বিন্যাসে | কী পরিবর্তনে |
| অসিঙ্ক্রোনাইজেশন | সিঙ্ক্রোনাস | কোরুটিন |
| কী | নেই | হ্যাঁ (vararg) |
| পরিষ্কার | নেই | স্বচালিত কোরুটিন বাতিল |
| সাধারণ ব্যবহার | Callbacks, Analytics, দৃশ্য সিঙ্ক্রোনাইজেশন | লোডিং, Flow সাবস্ক্রিপশন, টাইমার |
ব্যবহারিক ক্ষেত্রে, সাইড ইফেক্টের 70% ক্ষেত্রে LaunchedEffect (অসিঙ্ক্রোনাস কাজ, ডেটা লোডিং), 20% ক্ষেত্রে DisposableEffect (পরিষ্কার সহ সংস্থান) এবং কেবল 10% ক্ষেত্রে SideEffect (callback সিঙ্ক্রোনাইজেশন) ব্যবহৃত হয়। SideEffect কাজের একটি সীমিত পরিসরের জন্য একটি বিশেষায়িত সরঞ্জাম, কিন্তু সেই কাজগুলিতে এটি অপরিহার্য।
প্রধান ভুলটি হল SideEffect-এর মধ্যে Compose অবস্থা পরিবর্তন করা। যদিও SideEffect সরাসরি অসীম লুপের কারণ হয় না (কারণ এটি কম্পোজিশন দর্শার পর চালু হয়), এটি অতিরিক্ত পুনর্বিন্যাস ট্রিগার করতে পারে। যদি SideEffect-এর মধ্যে অবস্থা পরিবর্তিত হয় (mutableStateOf), এটি পরবর্তী ফ্রেমে একটি নতুন পুনর্বিন্যাস ট্রিগার করে, যা আবার SideEffect চালু করে — এবং এটি স্থিরীকরণ পর্যন্ত চলতে থাকে। এটি অসীম লুপ নয়, কিন্তু ফ্রেমওয়ার্কের জন্য অপ্রয়োজনীয় কাজ।
দ্বিতীয় ভুলটি হল SideEffect-এর মধ্যে ভারী গণনা করা। যেহেতু SideEffect প্রতিটি পুনর্বিন্যাসে ডাকা হয়, এবং পুনর্বিন্যাস প্রতি সেকেন্ডে কয়েক ডজন বার হতে পারে (অ্যানিমেশন, স্ক্রলিং-এর সময়), SideEffect-এর মধ্যে যে কোনো ভারী কোড ফ্রেম ড্রপের কারণ হবে। ভারী কাজ কম্পোজিশনের বাইরে সরান — একটি কোরুটিনে (LaunchedEffect) বা derivedStateOf / remember-এর মাধ্যমে গণনা করুন।
তৃতীয় ভুলটি হল SideEffect কে অসিঙ্ক্রোনাস কোডের জন্য ব্যবহার করা। SideEffect একটি suspend ফাংশন নয়, তাই delay(), await(), collect() এবং অন্যান্য কোরুটিন কাজ এর মধ্যে কম্পাইল হবে না। যদি পুনর্বিন্যাসের পর একটি অসিঙ্ক্রোনাস কাজ করার প্রয়োজন হয়, তবে LaunchedEffect-এর সাথে snapshotFlow { ... } ব্যবহার করুন, বা rememberCoroutineScope-এর মাধ্যমে একটি কোরুটিন শুরু করুন।
সাধারণ জিজ্ঞাসা
হ্যাঁ, SideEffect প্রতিটি সফল কম্পোজিশনে চালু হয়, যার মধ্যে প্রথমটিও অন্তর্ভুক্ত — যখন উপাদানটি প্রথমবারের জন্য পর্দায় আসে। এটি LaunchedEffect(Unit) থেকে ভিন্ন, যা প্রথম কম্পোজিশনে একবার চালু হয় কিন্তু পরবর্তী পুনর্বিন্যাসে চালু হয় না (যদি কী পরিবর্তিত না হয়)।
না, SideEffect কম্পোজিশন দর্শার পর চালু হয় — এর মধ্যে করা পরিবর্তনগুলি কেবল পরবর্তী ফ্রেমে প্রয়োগ হবে, যা লুপ প্রতিরোধ করে। তবে SideEffect-এর মধ্যে বারবার অবস্থা পরিবর্তন পুনর্বিন্যাসের হিমপাত সৃষ্টি করতে পারে, যা কর্মদক্ষতা হ্রাস করে। SideEffect-এর মধ্যে কেবল প্রয়োজন হলেই অবস্থা পরিবর্তন করুন।
SideEffect প্রতিটি পুনর্বিন্যাসে সিঙ্ক্রোনাসভাবে চালু হয়। snapshotFlow Compose অবস্থা থেকে একটি Flow তৈরি করে এবং প্রতিক্রিয়ামূলক পরিবর্তন পরিচালনার জন্য LaunchedEffect-এ collectLatest-এর সাথে ব্যবহার করা যেতে পারে। snapshotFlow সেই ক্ষেত্রগুলির জন্য উপযুক্ত যেখানে আপনার debounce, filter বা distinctUntilChanged-এর সাথে পরিবর্তনগুলিতে সাড়া দিতে হবে — যা সিঙ্ক্রোনাস SideEffect-এ অসম্ভব।
Android Studio Compose Modifier Debugger ব্যবহার করুন বা উপাদানের নাম এবং কল ফ্রিকোয়েন্সি সহ লগিং যোগ করুন। যদি SideEffect যতটা চিন্তা করা হয়েছিল তার চেয়ে বেশি চালু হয়, তবে যাচাই করুন কী মূল উপাদানের অবস্থা অপ্রয়োজনীয়ভাবে পরিবর্তিত হচ্ছে কিনা। অপ্টিমাইজেশন: পুনর্বিন্যাসের সংখ্যা কমাতে UI-এর স্থির অংশগুলিকে unstable এনোটেশন সহ পৃথক composable ফাংশনে বাহার করুন।
হ্যাঁ, এগুলি বিভিন্ন উদ্দেশ্যে একই উপাদানে ব্যবহার করা যেতে পারে। DisposableEffect একটি সংস্থান সেটআপ এবং পরিষ্কারের (একবার) জন্য দায়িত্বপ্রাপ্ত, যখন SideEffect প্রতিটি পুনর্বিন্যাসে সেই সংস্থানের সাথে বর্তমান অবস্থা সিঙ্ক্রোনাইজ করার দায়িত্ব নেয়। একটি সাধারণ উদাহরণ: DisposableEffect একটি API-এর মাধ্যমে একটি কলব্যাক রেজিস্ট্রিকরণ করে, এবং SideEffect প্রতিটি পরিবর্তনে সেই কলব্যাকে ক্যাপচার করা ডেটা আপডেট করে।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন