পাঠ ১৪ · ৫৮-এর মধ্যে · মডিউল ৪
Home / Courses / Full-Stack Web Frameworks / লোকাল vs গ্লোবাল স্টেট

লোকাল বনাম গ্লোবাল স্টেট

Local vs global state
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • লোকাল ও গ্লোবাল স্টেটের সংজ্ঞা এবং কোনটি কখন ব্যবহার করা উচিত তা ঠিক করার নিয়ম
  • প্রপ-ড্রিলিং সমস্যা — কেন এটি কোড পড়া ও পরিবর্তন করা কঠিন করে তোলে
  • গ্লোবাল/শেয়ার্ড স্টোর কীভাবে প্রপ-ড্রিলিং এড়ায় (M4-এর বাকি পাঠে এই ধারণাই সম্পূর্ণভাবে তৈরি হবে)
  • Python দিয়ে সত্যিকারের প্যারামিটার-গণনা — একই ডেটা একই গভীরতায় পাঠাতে দুই পদ্ধতিতে কতটি প্যারামিটার লাগে তার প্রকৃত হিসাব

১ · লোকাল স্টেট কী

লোকাল স্টেটLocal Stateএকটি নির্দিষ্ট কম্পোনেন্টের ভেতরে তৈরি ও পরিবর্তিত হওয়া ডেটা, যা শুধু সেই কম্পোনেন্টের রেন্ডারিং নিয়ন্ত্রণ করে। হলো এমন ডেটা যা একটি নির্দিষ্ট কম্পোনেন্টের মধ্যেই জন্ম নেয়, পরিবর্তিত হয়, এবং শুধু সেই কম্পোনেন্টের রেন্ডার আউটপুট প্রভাবিত করে। উদাহরণ — একটি ড্রপডাউন মেনু খোলা না বন্ধ, একটি ইনপুট বক্সে এখন পর্যন্ত কী টাইপ করা হয়েছে, বা একটি কাউন্টার বাটনের বর্তমান মান। এই ধরনের ডেটা অন্য কোনো কম্পোনেন্টের প্রয়োজন হয় না বলে একে অ্যাপ্লিকেশনের বাকি অংশে "এক্সপোজ" করার কোনো কারণ নেই।

২ · গ্লোবাল স্টেট কী, এবং প্রপ-ড্রিলিং সমস্যা

কিন্তু কিছু ডেটা একাধিক, অসম্পর্কিত কম্পোনেন্টে একসাথে দরকার হয় — যেমন বর্তমান লগ-ইন করা ইউজার, UI থিম (ডার্ক/লাইট), বা ভাষা সেটিং। এই ডেটা যদি শুধু টপ-লেভেল কম্পোনেন্টে রাখা হয় এবং নিচের একটি গভীর কম্পোনেন্ট তা ব্যবহার করতে চায়, তাহলে মাঝের প্রতিটি কম্পোনেন্টকে সেই ডেটা প্যারামিটার (prop) হিসেবে গ্রহণ করে শুধু আরও নিচে পাঠাতে হয় — এমনকি যদি মাঝের কম্পোনেন্টটি নিজে সেই ডেটা কখনো ব্যবহারই না করে। একেই বলে প্রপ-ড্রিলিংProp Drillingডেটা একটি গভীর কম্পোনেন্টে পৌঁছাতে মাঝের প্রতিটি কম্পোনেন্টের মধ্য দিয়ে প্যারামিটার হিসেবে "ড্রিল" করে পাঠানো, যদিও মাঝের কম্পোনেন্টগুলোর নিজের সেই ডেটার দরকার নেই।।

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

৩ · প্যারামিটার গুনে প্রমাণ — প্রপ-ড্রিলিং বনাম গ্লোবাল স্টোর

নিচের কোড সেলে একই দুটি তথ্য (theme ও user) একটি ৪ লেভেল গভীর কম্পোনেন্ট-ট্রিতে সবচেয়ে নিচের কম্পোনেন্টে পৌঁছাতে হবে। প্রথমে প্রপ-ড্রিলিং দিয়ে — প্রতিটি লেভেলের ফাংশন সিগনেচারে ডেটা প্যারামিটার হিসেবে থাকতে হয়। তারপর একটি সাধারণ গ্লোবাল স্টোর (dict) দিয়ে — যেখানে কোনো লেভেলেরই প্যারামিটার লাগে না, প্রতিটি ফাংশন সরাসরি স্টোর থেকে পড়ে। Python-এর inspect.signature() দিয়ে প্রতিটি পদ্ধতিতে প্রকৃতপক্ষে কতটি প্যারামিটার ব্যবহৃত হলো তা সত্যিকারের কোড দিয়ে গোনা হয়েছে।

Python
import inspect

# --- প্রথমে, একটি সাধারণ লোকাল স্টেট (তুলনার জন্য) ---
class CounterComponent:
    def __init__(self):
        self.state = {"count": 0}

    def increment(self):
        self.state["count"] += 1
        return self.state["count"]

counter = CounterComponent()
print("লোকাল স্টেট শুরু:", counter.state)
counter.increment()
counter.increment()
print("increment() দুইবার কল করার পর:", counter.state)
print("-" * 60)

# --- পদ্ধতি ১ -- প্রপ-ড্রিলিং (৪ লেভেল গভীর, ২টি ডেটা: theme, user) ---
def level4(theme, user):
    # শুধু এই সবচেয়ে গভীর কম্পোনেন্টটিই আসলে ডেটা ব্যবহার করে
    return f"Level4 রেন্ডার -- থিম={theme}, ইউজার={user}"

def level3(theme, user):
    return level4(theme, user)

def level2(theme, user):
    return level3(theme, user)

def level1_app(theme, user):
    return level2(theme, user)

drilling_functions = [level1_app, level2, level3, level4]
drilling_param_count = sum(
    len(inspect.signature(fn).parameters) for fn in drilling_functions
)

print("প্রপ-ড্রিলিং ফলাফল:", level1_app("dark", "রহিম"))
print("প্রপ-ড্রিলিং পদ্ধতিতে মোট প্যারামিটার (৪ লেভেল x ২টি ডেটা):", drilling_param_count)
print("-" * 60)

# --- পদ্ধতি ২ -- গ্লোবাল স্টোর (কোনো প্রপ-ড্রিলিং নেই) ---
global_store = {"theme": "dark", "user": "রহিম"}

def g_level4():
    return f"Level4 রেন্ডার -- থিম={global_store['theme']}, ইউজার={global_store['user']}"

def g_level3():
    return g_level4()

def g_level2():
    return g_level3()

def g_level1_app():
    return g_level2()

global_functions = [g_level1_app, g_level2, g_level3, g_level4]
global_param_count = sum(
    len(inspect.signature(fn).parameters) for fn in global_functions
)

print("গ্লোবাল স্টোর ফলাফল:", g_level1_app())
print("গ্লোবাল স্টোর পদ্ধতিতে মোট প্যারামিটার:", global_param_count)
print("-" * 60)

print(f"সারাংশ -- প্রপ-ড্রিলিং: {drilling_param_count}টি প্যারামিটার, "
      f"গ্লোবাল স্টোর: {global_param_count}টি প্যারামিটার (একই ফলাফলের জন্য)")

    
লক্ষ্য করুন প্রপ-ড্রিলিং পদ্ধতিতে level2 ও level3 ফাংশন দুটো নিজেরা কখনো theme বা user ব্যবহার করে না — তারা শুধু "মধ্যস্থতাকারী" হিসেবে ডেটা নিচে পাঠায়। ডেটার পরিমাণ বা গভীরতা বাড়লে (ধরুন ৮ লেভেল, ৫টি ডেটা) প্রপ-ড্রিলিং পদ্ধতিতে প্যারামিটার সংখ্যা রৈখিকভাবে বাড়তে থাকে (৮ x ৫ = ৪০), কিন্তু গ্লোবাল স্টোর পদ্ধতিতে তা সবসময় শূন্যই থাকে — এটিই মূল সমস্যা যা M4-এর বাকি পাঠগুলো (Flux/Redux প্যাটার্ন) সমাধান করবে।
মূল কথা · Key takeaway

সব স্টেট গ্লোবাল করে ফেলা ভুল পদ্ধতি — অপ্রয়োজনীয় জটিলতা তৈরি করে ও পারফরম্যান্স খরচ বাড়ায়। কিন্তু ডেটা যখন সত্যিই একাধিক অসম্পর্কিত অংশে দরকার হয়, তখন লোকাল স্টেট প্রপ-ড্রিলিং-এর মাধ্যমে জোর করে পাঠানোর চেষ্টা কোড রক্ষণাবেক্ষণকে কঠিন করে তোলে। পরের পাঠগুলোতে (L15-L17) আমরা দেখব কীভাবে Flux/Redux-স্টাইল স্টোর ও Context API এই সমস্যার দুটি ভিন্ন সমাধান দেয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ একটি মডাল ডায়ালগ খোলা আছে কি না — এই তথ্য কি লোকাল নাকি গ্লোবাল স্টেট হওয়া উচিত?

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

প্র ০২ প্রপ-ড্রিলিং কি সবসময় খারাপ? এটি এড়ানোর কোনো খরচ আছে কি?

না, সবসময় খারাপ নয় — মাত্র ১-২ লেভেল গভীরতার জন্য প্রপ-ড্রিলিং সম্পূর্ণ যুক্তিসঙ্গত ও সরল। সমস্যা তৈরি হয় গভীরতা বাড়লে বা ডেটার পরিমাণ বাড়লে। গ্লোবাল স্টোর ব্যবহার করলেও একটি খরচ আছে — যেকোনো কম্পোনেন্ট এখন "লুকানো" নির্ভরতা পায় (কোন কম্পোনেন্ট কোন গ্লোবাল ডেটার ওপর নির্ভরশীল তা কোড পড়ে সহজে বোঝা যায় না), তাই এটি একটি ট্রেড-অফ, একতরফা সমাধান নয়।

প্র ০৩ উপরের কোডে drilling_param_count এর মান ঠিক কীভাবে ৮ হয়?

প্রপ-ড্রিলিং পদ্ধতিতে ৪টি ফাংশন আছে (level1_app, level2, level3, level4) — প্রতিটির সিগনেচারে ঠিক ২টি প্যারামিটার আছে (theme, user)। inspect.signature(fn).parameters প্রতিটি ফাংশনের জন্য ২ রিটার্ন করে, আর ৪টি ফাংশন যোগ করলে মোট ৪ x ২ = ৮ হয়। গ্লোবাল স্টোর পদ্ধতির ৪টি ফাংশনের কোনোটিরই কোনো প্যারামিটার নেই, তাই সেই যোগফল ০।

অনুশীলন

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

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

  2. পরীক্ষা করুন: উপরের কোড সেলে গভীরতা ৪ থেকে বাড়িয়ে একটি level5 ফাংশন যোগ করুন যা theme ও user নেয় এবং level4-কে কল করে, তারপর drilling_functions লিস্টে সেটি যোগ করে Run চাপুন। নতুন drilling_param_count কত হলো?

    ৫টি ফাংশন x ২টি প্যারামিটার = ১০ হওয়া উচিত (আগে ছিল ৮)। এটি দেখায় প্রপ-ড্রিলিং-এর খরচ গভীরতার সাথে সরলরৈখিকভাবে (linearly) বাড়ে — প্রতিটি নতুন লেভেলের জন্য আরও ২টি প্যারামিটার যোগ হয় — অথচ গ্লোবাল স্টোর পদ্ধতিতে global_param_count গভীরতা যতই বাড়ুক না কেন সবসময় ০-ই থাকবে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
সিঙ্গেল-পেজ অ্যাপ্লিকেশনে রাউটিং