State Hoisting হল Jetpack Compose-এর একটি প্যাটার্ন যেখানে অবস্থা একটি চাইল্ড Composable ফাংশন থেকে প্যারেন্টে স্থানান্তরিত হয়, এবং চাইল্ড প্যারামিটারের মাধ্যমে ডেটা গ্রহণ করে এবং কলব্যাকের মাধ্যমে পরিবর্তন সম্পর্কে জানায়। এটি একমুখী ডেটা প্রবাহ (UDF) নীতির একটি বাস্তবায়ন, যেখানে অবস্থা উপরে উঠে এবং ইভেন্টগুলি নীচে নামে। Google Android Developers, 2026-এর মতে, State Hoisting কম্পোনেন্টগুলিকে পুনঃব্যবহারযোগ্য, পরীক্ষণযোগ্য এবং পূর্বানুমেয় করে তোলে।
মূল বিষয়
State Hoisting হল একটি প্যাটার্ন যেখানে একটি Composable ফাংশন অবস্থার মালিক হয় না বরং বাইরে থেকে তা গ্রহণ করে। ফাংশনের ভিতরে var-এর পরিবর্তে দুটি প্যারামিটার ব্যবহার করা হয়: প্রদর্শনের জন্য একটি মান এবং পরিবর্তনগুলি পরিচালনার জন্য একটি ল্যাম্বডা কলব্যাক। প্রযুক্তিগতভাবে, এর অর্থ চাইল্ড কম্পোনেন্ট স্টেটলেস (নিজস্ব অবস্থা ছাড়া) হয়ে যায়, যখন প্যারেন্ট স্টেটফুল (অবস্থার মালিক) হয়।
উদাহরণ: Material3-এর TextField কম্পোনেন্ট অভ্যন্তরীণভাবে প্রবেশ করানো টেক্সট সংরক্ষণ করে না। এটি value: String এবং onValueChange: (String) -> Unit গ্রহণ করে। TextField কলকারী প্যারেন্ট var value by remember { mutableStateOf("") } ঘোষণা করে এবং value ও onValueChange পাস করে। এটি ক্লাসিক State Hoisting: TextField একটি ডাম্ব কম্পোনেন্ট (শুধু প্রদর্শন করে এবং ইনপুট জানায়), প্যারেন্ট স্মার্ট (অবস্থার মালিক)।
স্টেটলেস বনাম স্টেটফুল: স্টেটলেস কম্পোনেন্ট পরীক্ষা করা সহজ — এটি অভ্যন্তরীণ অবস্থার উপর নির্ভর করে না, এর আচরণ সম্পূর্ণরূপে ইনপুট প্যারামিটার দ্বারা নির্ধারিত হয়। স্টেটফুল কম্পোনেন্ট দ্রুত প্রোটোটাইপিংয়ের জন্য সুবিধাজনক কিন্তু পুনঃব্যবহার করা কঠিন: এটি একটি একক ডেটা উৎসের সাথে দৃঢ়ভাবে সংযুক্ত থাকে। State Hoisting আপনাকে পছন্দ দেয়: অবস্থা উপরে সরিয়ে যেকোনো কম্পোনেন্টকে স্টেটলেস করা যেতে পারে।
UDF (একমুখী ডেটা প্রবাহ) একটি আর্কিটেকচারাল নীতি যেখানে ডেটা এক দিকে চলে: সত্যের উৎস (ViewModel বা প্যারেন্ট Composable) থেকে UI-তে, এবং ইভেন্টগুলি বিপরীত দিকে প্রবাহিত হয়। State Hoisting হল পৃথক কম্পোনেন্ট স্তরে UDF-এর বাস্তবায়ন। প্রতিটি কম্পোনেন্ট নিজের অবস্থা কখন এবং কীভাবে পরিবর্তন করবে তা সিদ্ধান্ত নেওয়ার পরিবর্তে, এটি প্যারেন্টকে একটি ইভেন্ট সম্পর্কে জানায়, এবং প্যারেন্ট সিদ্ধান্ত নেয় কীভাবে অবস্থা পরিবর্তন করতে হবে।
UDF-এর সুবিধা: পূর্বানুমেয়তা — অবস্থা কেবল একটি জায়গায় পরিবর্তিত হয়, যা রেস কন্ডিশন দূর করে; ট্রেসেবিলিটি — কল স্ট্যাক পরিবর্তনের শৃঙ্খল পুনর্নির্মাণ করতে দেয়; পরীক্ষণ — স্টেটফুল লজিক আলাদা ক্লাসে নেওয়া যেতে পারে এবং UI ছাড়া পরীক্ষা করা যেতে পারে। বড় প্রকল্পে, State Hoisting-এর সাথে UDF ডি ফ্যাক্টো স্ট্যান্ডার্ড।
সত্যের একক উৎস (Single Source of Truth) আরেকটি নীতি যা UDF-এর সাথে আসে। অবস্থার প্রতিটি অংশের ঠিক একটি উৎস থাকে। যদি দুটি কম্পোনেন্ট একই অবস্থা ব্যবহার করে, উৎসটি ভাগ করা উচিত (ViewModel বা সাধারণ প্যারেন্ট স্তরে)। State Hoisting নিশ্চিত করে যে উৎসটি শ্রেণিবিন্যাসে উপরে রয়েছে এবং অবস্থার নকল ঘটে না।
| দিক | কী পাস করা হয় | কীভাবে বাস্তবায়িত হয় |
|---|---|---|
| নীচে (প্যারেন্ট → চাইল্ড) | প্রদর্শনের জন্য মান | প্যারামিটার value: T |
| উপরে (চাইল্ড → প্যারেন্ট) | পরিবর্তন ইভেন্ট | প্যারামিটার onValueChange: (T) -> Unit |
মূল নিয়ম: অবস্থাকে ন্যূনতম সম্ভাব্য স্তরে উত্তোলন করা উচিত যা সকল কম্পোনেন্টের জন্য যথেষ্ট যাদের এটি প্রয়োজন। যদি অবস্থা কেবল একটি কম্পোনেন্টের ভিতরে ব্যবহার করা হয় — এটি স্থানীয় রাখুন। যদি দুটি সন্নিহিত কম্পোনেন্টের একই অবস্থার প্রয়োজন হয় — এটি সাধারণ প্যারেন্টে উত্তোলন করুন। যদি অবস্থা পুরো স্ক্রিন জুড়ে প্রয়োজন হয় — এটি ViewModel-এ উত্তোলন করুন।
ন্যূনতম উত্তোলন নিয়ম অপ্রয়োজনীয় জটিলতা প্রতিরোধ করে। ViewModel-এ টেক্সট ফিল্ডের অবস্থা উত্তোলনের কোনো মানে হয় না যদি এটি কেবল একটি স্ক্রিনের ভিতরে ব্যবহার করা হয় এবং Activity পুনঃনির্মাণে সংরক্ষিত না হয়। স্ক্রিন প্যারেন্ট স্তরে rememberSaveable ব্যবহার করুন, ViewModel-এ নয়, UI অবস্থার জন্য যা স্ক্রিন রোটেশন থেকে বাঁচতে হবে কিন্তু বিজনেস লজিকের প্রয়োজন নেই।
ViewModel-এ কখন উত্তোলন করবেন: যদি অবস্থা Activity পুনঃনির্মাণে টিকে থাকতে হয়, যদি একাধিক স্ক্রিনের প্রয়োজন হয়, যদি অবস্থা পরিবর্তন বিজনেস লজিক (নেটওয়ার্ক অনুরোধ, ডেটাবেস) ট্রিগার করে। ViewModel স্তরে State Hoisting MVVM আর্কিটেকচারে একটি মানক প্যাটার্ন, যেখানে UI স্তর স্টেটলেস এবং ViewModel স্টেটফুল।
// ❌ খারাপ: কম্পোনেন্ট তার নিজস্ব অবস্থার মালিক
@Composable
fun BadTextField(label: String) {
var text by remember { mutableStateOf("") }
TextField(value = text, onValueChange = { text = it }, label = { Text(label) })
}
// ✅ ভাল: State Hoisting — অবস্থা প্যারেন্টে
@Composable
fun GoodTextField(value: String, onValueChange: (String) -> Unit, label: String) {
TextField(value = value, onValueChange = onValueChange, label = { Text(label) })
}
// ব্যবহার: প্যারেন্ট অবস্থার মালিক
@Composable
fun Form() {
var name by rememberSaveable { mutableStateOf("") }
GoodTextField(value = name, onValueChange = { name = it }, label = "Name")
}
একটি লগইন স্ক্রিন বিবেচনা করুন যাতে দুটি ফিল্ড (ইমেইল, পাসওয়ার্ড) এবং একটি বাটন রয়েছে। তিনটি কম্পোনেন্টই State Hoisting-এর মাধ্যমে অবস্থা গ্রহণ করে: ইমেইল এবং পাসওয়ার্ড প্যারেন্ট দ্বারা পরিচালিত হয়, বাটন তার enabled অবস্থা একটি মান হিসাবে গ্রহণ করে।
// স্ক্রিন স্তরে State Hoisting
@Composable
fun LoginScreen(viewModel: LoginViewModel) {
val uiState by viewModel.uiState.collectAsState()
Column(modifier = Modifier.padding(16.dp)) {
// ইমেইল ফিল্ড — ল্যাম্বডার মাধ্যমে State Hoisting
EmailField(
email = uiState.email,
onEmailChange = { viewModel.onEmailChanged(it) }
)
// পাসওয়ার্ড ফিল্ড — একইভাবে
PasswordField(
password = uiState.password,
onPasswordChange = { viewModel.onPasswordChanged(it) }
)
// বাটন — শুধু enabled গ্রহণ করে (শুধু পঠনযোগ্য)
LoginButton(enabled = uiState.isFormValid, onClick = viewModel::login)
}
}
// স্টেটলেস কম্পোনেন্ট: ইমেইল + কলব্যাক গ্রহণ করে
@Composable
fun EmailField(email: String, onEmailChange: (String) -> Unit) {
OutlinedTextField(
value = email,
onValueChange = onEmailChange,
label = { Text("ইমেইল") },
singleLine = true
)
}
// স্টেটলেস বাটন কম্পোনেন্ট
@Composable
fun LoginButton(enabled: Boolean, onClick: () -> Unit) {
Button(onClick = onClick, enabled = enabled) {
Text("লগইন")
}
}
EmailField এবং PasswordField সম্পূর্ণ স্টেটলেস। এগুলি যেকোনো ডেটা উৎসের সাথে সংযুক্ত করে যেকোনো স্ক্রিনে পুনঃব্যবহার করা যেতে পারে। LoginButton enabled কে শুধু পঠনযোগ্য হিসাবে গ্রহণ করে — এটি State Hoisting-এর আরেকটি রূপ যেখানে অবস্থা উত্তোলিত হয় না (বাটন নিজেকে সক্ষম করতে পারে না) বরং প্রস্তুত অবস্থায় নীচে পাঠানো হয়। এই পদ্ধতি ন্যূনতম কম্পোনেন্ট যুগলের সাথে সর্বোচ্চ নমনীয়তা প্রদান করে।
প্রতিটি অবস্থা উত্তোলনের প্রয়োজন নেই। স্থানীয় অবস্থা (Composable-এর ভিতরে State) ন্যায্য যখন: ডেটা কেবল একটি কম্পোনেন্টের ভিতরে প্রয়োজন, এটি প্রতিবেশী উপাদানগুলিকে প্রভাবিত করে না, এবং এটি কোনো নির্দিষ্ট বিভাগের পুনঃসংযোজন থেকে বাঁচার প্রয়োজন নেই। উদাহরণস্বরূপ, অ্যানিমেশন অবস্থা, ইনপুট ফিল্ড ফোকাস, বর্তমান স্ক্রোল অবস্থান — এগুলি স্থানীয় রাখা যুক্তিসঙ্গত।
কখন State Hoisting প্রয়োজনীয়: অবস্থা একাধিক চাইল্ড কম্পোনেন্ট দ্বারা ব্যবহৃত হয়; একটি চাইল্ডে পরিবর্তন অন্যটিতে প্রতিফলিত হওয়া উচিত; অবস্থা পরিবর্তনের লজিক UI থেকে আলাদাভাবে পরীক্ষা করা প্রয়োজন; অবস্থা Activity পুনঃনির্মাণে টিকে থাকা উচিত। এই ক্ষেত্রে, স্থানীয় অবস্থা ডেটা নকল এবং অসঙ্গতি তৈরি করে।
হাইব্রিড পদ্ধতি: ন্যূনতম অবস্থা স্থানীয় রাখুন, বাকি উত্তোলন করুন। Compose-এর নিয়ম: “অবস্থা যতটা প্রয়োজন ততটা উপরে এবং যতটা সম্ভব ততটা নীচে উত্তোলন করুন।” অনুশীলনে, এর অর্থ স্থানীয় remember দিয়ে শুরু করা এবং শুধুমাত্র যখন অন্য কম্পোনেন্ট থেকে অ্যাক্সেস প্রয়োজন তখন স্তর উত্তোলন করা। State Hoisting পূর্বসক্রিয়ভাবে প্রয়োগ করবেন না — এটি প্রয়োজন ছাড়াই কোড জটিল করে।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
State Hoisting একটি UI কম্পোনেন্ট-স্তরের প্যাটার্ন। ViewModel ব্যবসায়িক লজিকের জন্য একটি আর্কিটেকচারাল স্তর। State Hoisting প্যারেন্ট Composable স্তর, স্ক্রিন স্তর বা ViewModel-এ অবস্থা উত্তোলন করতে পারে। ViewModel হল সেই অবস্থার জন্য সর্বোচ্চ উত্তোলন বিন্দু যা Activity পুনঃনির্মাণে টিকে থাকতে হবে।
একটি স্টেটলেস কম্পোনেন্ট কেবল মান পাস করে পরীক্ষা করা হয়। প্রয়োজনীয় প্যারামিটার সহ Composable কল করুন এবং ComposeTestRule-এর মাধ্যমে প্রদর্শন যাচাই করুন। অবস্থা পরিবর্তন প্যারেন্ট বা ViewModel স্তরে পরীক্ষা করা হয় — UI থেকে আলাদা। এটি পরীক্ষাগুলি উল্লেখযোগ্যভাবে সহজ করে: কম্পোনেন্টের ভিতরে পুনঃসংযোজন অনুকরণের প্রয়োজন নেই।
হ্যাঁ, এটি সাধারণ অভ্যাস। যদি কোনো কম্পোনেন্টের কেবল ডেটা প্রদর্শনের প্রয়োজন হয় পরিবর্তনের ক্ষমতা ছাড়া — State<T> (শুধু পঠনযোগ্য) পাস করুন। কম্পোনেন্ট পরিবর্তনগুলিতে সাবস্ক্রাইব হবে কিন্তু সেগুলি শুরু করতে পারবে না। এটি এনক্যাপসুলেশন শক্তিশালী করে এবং ডেটাকে অনাকাঙ্ক্ষিত মিউটেশন থেকে রক্ষা করে।
গভীর পাসিংয়ের জন্য CompositionLocal বা প্যারেন্ট Composable প্যারামিটারের মাধ্যমে পাস করুন। যদি পুরো স্ক্রিন জুড়ে অবস্থার প্রয়োজন হয় — এটি ViewModel-এ নিন এবং collectAsState() ব্যবহার করুন। 5+ স্তরের মাধ্যমে পাস করা ভুল আর্কিটেকচারের লক্ষণ; কম্পোনেন্ট শ্রেণিবিন্যাস পুনর্বিবেচনা করুন।
State Hoisting পুনঃসংযোজনের সংখ্যা সামান্য বাড়াতে পারে যেহেতু প্যারেন্টে পরিবর্তন সমস্ত চাইল্ডকে পুনঃসংযোজিত করতে পারে। পরিবর্তন ফিল্টার করতে derivedStateOf এবং লক্ষ্যযুক্ত আপডেটের জন্য LazyColumn-এ keys ব্যবহার করুন। বেশিরভাগ পরিস্থিতিতে, State Hoisting-এর ওভারহেড রক্ষণাবেক্ষণযোগ্যতার সুবিধার তুলনায় নগণ্য।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন