পাঠ ০৯ · ৫৮-এর মধ্যে · মডিউল ৩
Home / Courses / Full-Stack Web Frameworks / কম্পোনেন্ট-ভিত্তিক UI

কম্পোনেন্ট-ভিত্তিক UI — মূল ধারণা

Component-based UI: the core idea
৯ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

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

  • কম্পোনেন্ট কাকে বলে এবং কেন ফ্রন্ট-এন্ড ফ্রেমওয়ার্কের কেন্দ্রীয় বিল্ডিং ব্লক এটি
  • Props কী — কম্পোনেন্টের ইনপুট হিসেবে ডেটা কীভাবে পাস করা হয়
  • কম্পোজিশন — ছোট কম্পোনেন্ট থেকে কীভাবে বড়, জটিল UI তৈরি হয়
  • Python ফাংশন দিয়ে vnode-স্টাইল কম্পোনেন্ট বাস্তবে লিখে দেখা ও কম্পোজ করা

১ · কম্পোনেন্ট কাকে বলে

কম্পোনেন্টComponentUI-এর একটি স্বনির্ভর, পুনর্ব্যবহারযোগ্য একক — কিছু ইনপুট (props) নেয়, তার ভিত্তিতে রেন্ডার-করা আউটপুট তৈরি করে। হলো UI-এর একটি স্বনির্ভর টুকরো — একটি বাটন, একটি ইউজার কার্ড, একটি নেভিগেশন বার, এমনকি পুরো একটি পেজও একটি কম্পোনেন্ট হতে পারে। প্রতিটি কম্পোনেন্ট মূলত একটি ফাংশনের মতো আচরণ করে: কিছু ইনপুট নেয়, আর সেই ইনপুটের ভিত্তিতে একটি নির্দিষ্ট আউটপুট (কী রেন্ডার হবে, তার বর্ণনা) রিটার্ন করে। একই ইনপুট দিলে একই আউটপুট পাওয়া উচিত — এই পূর্বানুমানযোগ্যতাই কম্পোনেন্টকে পুনর্ব্যবহারযোগ্য করে তোলে।

React, Vue, Angular — সিনট্যাক্স আলাদা হলেও তিনটিরই কেন্দ্রীয় ধারণা এটাই: UI-কে ছোট ছোট, স্বাধীনভাবে পরীক্ষাযোগ্য, পুনর্ব্যবহারযোগ্য টুকরোয় ভাগ করা, তারপর সেগুলো একত্র করে সম্পূর্ণ অ্যাপ্লিকেশন গড়ে তোলা।

২ · Props — কম্পোনেন্টে ডেটা পাঠানো

একটি কম্পোনেন্ট কীভাবে জানে কী রেন্ডার করতে হবে? এর জন্য প্যারেন্ট কম্পোনেন্ট তাকে props (properties সংক্ষেপে) পাঠায় — অনেকটা একটি ফাংশনের আর্গুমেন্টের মতো। একটি UserCard কম্পোনেন্ট নিজে জানে না কোন ইউজারের নাম দেখাতে হবে — সেটা তাকে props হিসেবে দেওয়া হয় (name="রহিম")।

Props ভেতরে ঢোকে
প্যারেন্ট কম্পোনেন্ট থেকে চাইল্ড কম্পোনেন্টে ডেটা একমুখী প্রবাহে যায় — ঠিক ফাংশন কলের আর্গুমেন্টের মতো।
Props অপরিবর্তনীয়
যে কম্পোনেন্ট props পায়, সে নিজে সেই props পরিবর্তন করে না — শুধু পড়ে ও তার ভিত্তিতে রেন্ডার করে (L11-এ props বনাম state বিস্তারিত দেখব)।

৩ · কম্পোজিশন — ছোট কম্পোনেন্ট দিয়ে বড় UI গড়া

একটি কম্পোনেন্ট তার রেন্ডার-করা কাঠামোর ভেতরে অন্য কম্পোনেন্টকে বসাতে পারে — ঠিক যেমন একটি ফাংশন আরেকটি ফাংশনকে কল করে তার রিটার্ন-ভ্যালু ব্যবহার করে। এভাবে ছোট, একক-দায়িত্বশীল কম্পোনেন্ট (Avatar, Button) একত্র হয়ে একটি মাঝারি কম্পোনেন্ট (UserCard) গড়ে তোলে, আর অনেকগুলো UserCard একত্র হয়ে একটি বড় কম্পোনেন্ট (UserList) গড়ে তোলে — এভাবেই একটি সম্পূর্ণ পেজ পর্যন্ত তৈরি হয়ে যায়।

UserList রুট কম্পোনেন্ট UserCard #1 UserCard #2 (...) Avatar তথ্য (h3+p) Button
UserList → একাধিক UserCard কম্পোজ করে; প্রতিটি UserCard → Avatar, তথ্য ও Button কম্পোজ করে। প্রতিটি বক্স একটি স্বাধীন, পুনর্ব্যবহারযোগ্য কম্পোনেন্ট।

৪ · Python কোডে কম্পোনেন্ট — ফাংশন যা vnode রিটার্ন করে

যেহেতু ব্রাউজারের Pyodide স্যান্ডবক্সে সত্যিকারের DOM নেই, তাই আমরা প্রতিটি কম্পোনেন্টের রেন্ডার-করা আউটপুটকে একটি ছোট Python dict দিয়ে উপস্থাপন করব — এই কাঠামোকে বলা হবে vnode ("ভার্চুয়াল নোড")। প্রতিটি "কম্পোনেন্ট" একটি সাধারণ Python ফাংশন যা props আর্গুমেন্ট হিসেবে নেয় এবং একটি vnode dict রিটার্ন করে — L10-এ আমরা এই একই কাঠামো নিয়ে ভার্চুয়াল DOM ডিফিং শিখব।

Python
# vnode কাঠামো: {"tag": ..., "props": {...}, "children": [...]}
# প্রতিটি কম্পোনেন্ট একটি ফাংশন -- props নেয়, vnode রিটার্ন করে

def Avatar(name, url):
    """ছোট, একক-দায়িত্বশীল কম্পোনেন্ট -- শুধু একটি ছবি রেন্ডার করে"""
    return {"tag": "img", "props": {"src": url, "alt": name}, "children": []}


def Button(label, variant="primary"):
    """আরেকটি ছোট কম্পোনেন্ট -- একটি বাটন রেন্ডার করে"""
    return {"tag": "button", "props": {"class": f"btn btn-{variant}"}, "children": [label]}


def UserCard(name, email, avatar_url):
    """মাঝারি কম্পোনেন্ট -- Avatar ও Button কম্পোজ করে গঠিত"""
    return {
        "tag": "div",
        "props": {"class": "user-card"},
        "children": [
            Avatar(name, avatar_url),
            {
                "tag": "div",
                "props": {"class": "user-info"},
                "children": [
                    {"tag": "h3", "props": {}, "children": [name]},
                    {"tag": "p", "props": {}, "children": [email]},
                ],
            },
            Button("প্রোফাইল দেখুন", variant="secondary"),
        ],
    }


def UserList(users):
    """সবচেয়ে বড় কম্পোনেন্ট -- একাধিক UserCard কম্পোজ করে"""
    return {
        "tag": "div",
        "props": {"class": "user-list"},
        "children": [UserCard(**u) for u in users],
    }


def print_vnode(vnode, indent=0):
    """vnode ট্রি রিকার্সিভভাবে প্রিন্ট করার সাহায্যকারী ফাংশন"""
    pad = "  " * indent
    if isinstance(vnode, str):
        print(f"{pad}\"{vnode}\"")
        return
    props_str = f" props={vnode['props']}" if vnode["props"] else ""
    print(f"{pad}<{vnode['tag']}>{props_str}")
    for child in vnode["children"]:
        print_vnode(child, indent + 1)


# তিনজন ইউজারের ডেটা -- এটা আসতে পারত একটি API রেসপন্স থেকে
users_data = [
    {"name": "রহিম উদ্দিন", "email": "rahim@example.com", "avatar_url": "/img/rahim.png"},
    {"name": "করিম আহমেদ", "email": "karim@example.com", "avatar_url": "/img/karim.png"},
]

app_tree = UserList(users_data)

print("সম্পূর্ণ কম্পোজড UI-ট্রি:\n")
print_vnode(app_tree)

# যাচাই: root কম্পোনেন্ট ২টি UserCard কম্পোজ করেছে কি না
card_count = len(app_tree["children"])
print(f"\nমোট কম্পোজ-করা UserCard সংখ্যা: {card_count}")
print(f"প্রতিটি UserCard-এর সন্তান সংখ্যা: {len(app_tree['children'][0]['children'])} (Avatar + তথ্য + Button)")

    
লক্ষ্য করুন UserCard ফাংশন নিজে কখনো জানে না কয়জন ইউজার আছে বা তাদের নাম কী — সেই তথ্য প্রতিবার props হিসেবে (name, email, avatar_url) পাস করা হয়েছে। আর UserList নিজে কোনো <img> বা <button> ট্যাগ সরাসরি জানে না — সে শুধু UserCard কে কল করেছে, যেটা নিজে Avatar আর Button কল করেছে। এই স্তরে-স্তরে কম্পোজিশনই কম্পোনেন্ট-ভিত্তিক UI-এর মূল শক্তি।
মূল কথা · Key takeaway

একটি কম্পোনেন্ট হলো একটি ফাংশনের মতো: props ইনপুট হিসেবে নেয়, রেন্ডার-করা আউটপুট (vnode) রিটার্ন করে। ছোট, একক-দায়িত্বশীল কম্পোনেন্ট একসাথে কম্পোজ হয়ে জটিল UI তৈরি করে — ঠিক যেমন ছোট ফাংশন একসাথে কম্পোজ হয়ে জটিল প্রোগ্রাম তৈরি করে। এই একই vnode কাঠামো নিয়ে পরের পাঠে আমরা ভার্চুয়াল DOM ডিফিং অ্যালগরিদম তৈরি করব।

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

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

প্র ০১ কোড সেলে Avatar ও Button কেন আলাদা ফাংশন করে লেখা হলো — সবকিছু UserCard-এর ভেতরেই তো সরাসরি লেখা যেত?

আলাদা ফাংশন করার কারণ পুনর্ব্যবহারযোগ্যতা ও একক-দায়িত্ব। Avatar ও Button কম্পোনেন্ট শুধু UserCard-এই নয়, অ্যাপের অন্য যেকোনো জায়গায় (যেমন একটি নেভিগেশন বার বা একটি ফর্ম) পুনর্ব্যবহার করা যাবে। সব লজিক একটি বিশাল ফাংশনে লিখলে সেটি পড়া, টেস্ট করা ও পুনর্ব্যবহার করা কঠিন হয়ে যায় — ছোট কম্পোনেন্টে ভাগ করলে প্রতিটি অংশ আলাদাভাবে বোঝা ও পরিবর্তন করা সহজ হয়।

প্র ০২ UserList ফাংশনের ভেতরে [UserCard(**u) for u in users] লাইনটি আসলে কী করছে?

এটি users লিস্টের প্রতিটি ইউজার-dict-এর জন্য UserCard ফাংশনকে একবার করে কল করছে, **u দিয়ে dict-টিকে কী-ওয়ার্ড আর্গুমেন্ট হিসেবে (name=..., email=..., avatar_url=...) স্প্রেড করছে। ফলাফল হলো প্রতিটি ইউজারের জন্য একটি করে vnode — এই লিস্টটাই UserList-এর children হয়ে যায়। এটাই দেখায় কীভাবে ডেটার একটি লিস্ট থেকে কম্পোনেন্টের একটি লিস্ট (vnode-এর লিস্ট) তৈরি হয়, ঠিক যেমন React-এ .map() দিয়ে করা হয়।

প্র ০৩ যদি Avatar(name, url) ফাংশনে ভুল করে url এর বদলে None পাস করা হয়, তাহলে কী পরিবর্তন হয় — কম্পোনেন্টের গঠন না props-এর মান?

শুধু props-এর মান পরিবর্তন হয় — vnode["props"]["src"] এখন None হবে। কম্পোনেন্টের গঠন (এটি একটি img ট্যাগ, কোনো children নেই) একই থাকে। এটাই দেখায় props শুধু ডেটা ইনপুট — কম্পোনেন্টের নিজস্ব গঠন/লজিক (তার ফাংশন বডি) props পরিবর্তনে বদলায় না, শুধু আউটপুটের ডেটা বদলায়।

অনুশীলন

  1. চিন্তা করুন: আপনার ব্যবহৃত কোনো ওয়েবসাইট বা অ্যাপ (যেমন Facebook, YouTube) মনে করুন — সেখানে কোন কোন অংশকে আপনি আলাদা, পুনর্ব্যবহৃত কম্পোনেন্ট হিসেবে চিহ্নিত করতে পারেন?

    উদাহরণস্বরূপ, YouTube-এ প্রতিটি ভিডিও-থাম্বনেইল কার্ড (থাম্বনেইল + শিরোনাম + চ্যানেল নাম + ভিউ কাউন্ট) একই কম্পোনেন্টের বারবার পুনর্ব্যবহার — প্রতিবার শুধু props (ভিডিওর ডেটা) আলাদা। "লাইক" বাটন, কমেন্ট বক্স, নেভিগেশন সাইডবার — এগুলোও প্রতিটি স্বাধীন, পুনর্ব্যবহৃত কম্পোনেন্ট। এভাবে প্রায় যেকোনো UI-কেই পুনরাবৃত্ত, স্বাধীন টুকরোয় ভাঙা যায়।

  2. পরীক্ষা করুন: উপরের কোড সেলে একটি নতুন কম্পোনেন্ট ফাংশন Badge(text) লিখুন যা {"tag": "span", "props": {"class": "badge"}, "children": [text]} রিটার্ন করে, তারপর UserCard-এর children লিস্টে Badge("যাচাইকৃত") যোগ করুন এবং Run চেপে দেখুন প্রতিটি ইউজার কার্ডের সন্তান সংখ্যা কীভাবে ৩ থেকে ৪ হয়ে যায়।

    Badge("যাচাইকৃত") যোগ করার পর UserCard-এর children লিস্টে চারটি vnode থাকবে (Avatar, তথ্য div, Button, আর নতুন Badge) — তাই len(app_tree['children'][0]['children']) এখন 4 প্রিন্ট হবে, 3 নয়। এটাই দেখায় নতুন কম্পোনেন্ট কম্পোজ করা মানে বিদ্যমান স্ট্রাকচার ভাঙা নয়, শুধু children লিস্টে একটি নতুন এন্ট্রি যোগ করা।

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

  • পরের পাঠ L10 এই একই vnode কাঠামো ব্যবহার করে সত্যিকারের ভার্চুয়াল DOM ডিফিং অ্যালগরিদম তৈরি করা হবে।
  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
  • JavaScript Programming কোর্স সহোদর কোর্স এই কোর্সের ফ্রন্ট-এন্ড ফ্রেমওয়ার্ক মডিউলগুলোর ভাষাগত ভিত্তি সেই কোর্সেই তৈরি হয়েছে।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks — সব এক জায়গায়।
আগের পাঠ
API গেটওয়ে ও ব্যাক-এন্ড-ফর-ফ্রন্ট-এন্ড (BFF) প্যাটার্ন