কাস্টম কম্পোনেন্ট ও পুনঃব্যবহারযোগ্যতা
এই পাঠে যা শিখবেন
- ছোট উইজেট থেকে বড়, পুনঃব্যবহারযোগ্য কাস্টম কম্পোনেন্ট বানানোর ধারণা
- একটি কাস্টম কম্পোনেন্ট আসলে "প্যারামিটারাইজড উইজেট-সাবট্রি তৈরি করা একটি ফাংশন" — এর বেশি কিছু নয়
- Python দিয়ে একটি
Cardকম্পোনেন্ট তৈরি ও তিনবার ভিন্ন ডেটায় ইনস্ট্যান্স করা - একই কম্পোনেন্ট সংজ্ঞা কীভাবে ভিন্ন ভিন্ন প্রকৃত আউটপুট তৈরি করে তা রেন্ডার-আউটপুট দিয়ে যাচাই
১ · কাস্টম কম্পোনেন্ট মানে কী
L20-এ আমরা দেখেছিলাম Text, Input, Button-এর মতো ছোট, বিল্ট-ইন-স্টাইল
উইজেট ফাংশন কম্পোজ করে একটি স্ক্রিন বানানো যায়। কিন্তু একই প্যাটার্নের উইজেট-গ্রুপ যদি অ্যাপের বিভিন্ন জায়গায়
বারবার দরকার হয় (যেমন একটি "প্রোডাক্ট কার্ড" — একটি শিরোনাম + একটি সাবটাইটেল, একটি বক্সে মোড়ানো), তাহলে
প্রতিবার হাতে সেই একই গঠন লেখা অপচয় ও ভুলের ঝুঁকিপূর্ণ। এখানেই
কাস্টম কম্পোনেন্টCustom Componentছোট উইজেট কম্পোজ করে বানানো একটি প্যারামিটারাইজড ফাংশন, যা কল হলে একটি সম্পূর্ণ উইজেট-সাবট্রি রিটার্ন করে -- বারবার ভিন্ন ডেটা দিয়ে পুনঃব্যবহারযোগ্য।
কাজে লাগে — এটি আসলে শুধু একটি ফাংশন, যা প্যারামিটার নেয় ও L20-এর একই
{"type", "props", "children"} শেপে একটি উইজেট-সাবট্রি রিটার্ন করে।
কাস্টম কম্পোনেন্ট প্যারামিটার নেয় (title, subtitle, onPress) — একই সংজ্ঞা, ভিন্ন ইনপুট, ভিন্ন আউটপুট।
একবার সংজ্ঞায়িত হলে অ্যাপের যেকোনো স্ক্রিনে যতবার প্রয়োজন ইনস্ট্যান্স করা যায় — কোড ডুপ্লিকেশন ছাড়াই।
বড় কম্পোনেন্ট ছোট কম্পোনেন্ট দিয়ে গঠিত হতে পারে — কাস্টম কম্পোনেন্টও নিজেই আরেকটি বড় কাস্টম কম্পোনেন্টের চাইল্ড হতে পারে।
২ · একটি Card কম্পোনেন্ট বানানো ও তিনবার ব্যবহার করা
নিচের কোড সেলে আমরা L20-এর মতোই কয়েকটি বেসিক উইজেট ফাংশন সংজ্ঞায়িত করব, তারপর সেগুলো দিয়ে একটি
Card(title, subtitle) কাস্টম কম্পোনেন্ট বানাব। এই একই Card ফাংশনকে তিনবার ভিন্ন
ভিন্ন ডেটা দিয়ে কল করে তিনটি ভিন্ন কার্ড-ইনস্ট্যান্স তৈরি করব, তারপর একটি রিকার্সিভ render
ফাংশন দিয়ে তিনটিই প্রকৃত "draw operation" লিস্টে রূপান্তর করে দেখাব — প্রমাণ করব একই কম্পোনেন্ট সংজ্ঞা সত্যিই
ভিন্ন কনক্রিট আউটপুট তৈরি করছে।
# বেসিক উইজেট ফাংশন (L20-এর একই শেপ) + একটি কাস্টম Card কম্পোনেন্ট
def Text(content):
return {"type": "Text", "props": {"content": content}, "children": []}
def Container(*children, direction="column"):
return {"type": "Container", "props": {"direction": direction}, "children": list(children)}
def Card(title, subtitle):
"""কাস্টম কম্পোনেন্ট -- ছোট উইজেট কম্পোজ করে একটি সম্পূর্ণ উইজেট-সাবট্রি রিটার্ন করে।
title/subtitle প্যারামিটারের ভিত্তিতে ভিন্ন ভিন্ন কনক্রিট আউটপুট তৈরি হয়।"""
return {
"type": "Card",
"props": {"title": title, "subtitle": subtitle},
"children": [
Text(title),
Text(subtitle),
],
}
# একই Card() সংজ্ঞা -- তিনবার ভিন্ন ভিন্ন ডেটা দিয়ে ইনস্ট্যান্স করা
card1 = Card("প্রোডাক্ট: হেডফোন", "মূল্য: ১,২০০ টাকা")
card2 = Card("প্রোডাক্ট: কীবোর্ড", "মূল্য: ২,৫০০ টাকা")
card3 = Card("প্রোডাক্ট: মাউস", "মূল্য: ৬০০ টাকা")
cards = [card1, card2, card3]
def render(widget, x=0, y=0):
"""রিকার্সিভভাবে ট্রি ওয়াক করে প্রকৃত draw-operation লিস্ট তৈরি করে (L20-এর print_tree-এর মতো ধারণা,
কিন্তু এখানে সরাসরি ড্র-অপারেশন ট্যুপল রিটার্ন করে)"""
ops = []
row_height = 22
if widget["type"] == "Card":
card_height = row_height * len(widget["children"]) + 12
ops.append(("draw_rect", x, y, 220, card_height))
inner_y = y + 8
for child in widget["children"]:
ops.extend(render(child, x + 12, inner_y))
inner_y += row_height
elif widget["type"] == "Text":
ops.append(("draw_text", x, y, widget["props"]["content"]))
return ops
print("=== তিনটি ভিন্ন Card ইনস্ট্যান্স রেন্ডার করা হচ্ছে ===\n")
cursor_y = 0
for i, card in enumerate(cards, start=1):
ops = render(card, x=0, y=cursor_y)
print(f"-- Card ইনস্ট্যান্স #{i} --")
for op in ops:
print(f" {op}")
cursor_y += 90
print(f"\nমোট ইনস্ট্যান্স সংখ্যা: {len(cards)}")
print(f"প্রতিটি ইনস্ট্যান্সের title আলাদা: {[c['props']['title'] for c in cards]}")
Card ফাংশনের সংজ্ঞা মাত্র একবার লেখা হয়েছে, কিন্তু card1,
card2, card3 — প্রতিটির props["title"], props["subtitle"]
এবং render()-এর আউটপুটে থাকা প্রকৃত draw_text কন্টেন্ট আলাদা। এটিই পুনঃব্যবহারযোগ্যতার
মূল সুবিধা — একটি সংজ্ঞা, একাধিক স্বতন্ত্র ব্যবহার, প্রতিটি নিজের ডেটা অনুযায়ী সঠিক আউটপুট তৈরি করে, কোনো
কোড কপি-পেস্ট ছাড়াই।
একটি কাস্টম কম্পোনেন্ট আসলে জাদুকরী কিছু নয় — এটি শুধু একটি ফাংশন যা প্যারামিটার নেয় এবং ছোট উইজেট কম্পোজ করে একটি উইজেট-সাবট্রি রিটার্ন করে (L20-এর একই শেপ অনুসরণ করে)। এই একই সংজ্ঞা বারবার, ভিন্ন ভিন্ন ডেটা দিয়ে ইনস্ট্যান্স করা যায় — এটিই কেন React/Flutter/SwiftUI-স্টাইল ফ্রেমওয়ার্কে বড় অ্যাপও শেষ পর্যন্ত মুষ্টিমেয় কিছু পুনঃব্যবহারযোগ্য কম্পোনেন্টের সমন্বয়ে গঠিত হতে পারে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
উপরের কোড সেলে card1, card2, card3 — এই তিনটি ডিকশনারির "type" ভ্যালু কি একই না ভিন্ন?
একই — তিনটিরই "type" হলো "Card", কারণ তিনটিই একই Card() ফাংশন
কল করে তৈরি হয়েছে। যা ভিন্ন তা হলো "props"-এর ভেতরের মান (title,
subtitle) এবং সেই কারণে children-এর ভেতরের Text নোডগুলোর
কন্টেন্ট — গঠন (structure) একই, ডেটা ভিন্ন।
প্র ০২
যদি Card-এ একটি তৃতীয় প্যারামিটার on_press (একটি ফাংশন) যোগ করা হয়, এটি কি পুনঃব্যবহারযোগ্যতা নীতির সাথে সামঞ্জস্যপূর্ণ হবে?
হ্যাঁ — এটিই ঠিক এই নীতির শক্তি। প্রতিটি Card ইনস্ট্যান্স তার নিজস্ব on_press
ফাংশন দিতে পারবে (যেমন card1 একটি প্রোডাক্ট পেজে নিয়ে যাবে, card2 অন্য একটিতে),
অথচ Card-এর মূল সংজ্ঞা (গঠন, লেআউট) একটাই থাকবে — একই কম্পোনেন্ট, ভিন্ন আচরণ, শুধু
প্যারামিটারের মাধ্যমে।
প্র ০৩
render() ফাংশনটি কি Card সম্পর্কে "জানে" যে এটি তিনবার ভিন্ন ডেটায় ব্যবহৃত হচ্ছে?
না — render()-এর কোনো ধারণাই নেই যে এটি কতবার বা কোন কোন ডেটায় কল হয়েছে। এটি শুধু একটি
নির্দিষ্ট উইজেট-ডিকশনারি (যা তাকে পাস করা হয়েছে) ওয়াক করে draw-অপারেশন তৈরি করে। "তিনবার ভিন্ন
ইনস্ট্যান্স" ধারণাটি সম্পূর্ণভাবে কল করার পক্ষ থেকে (for card in cards লুপ) আসে, নিজে
render()-এর ভেতরে নয় — এটিই দেখায় কেন কম্পোনেন্ট ও তার রেন্ডারিং লজিক আলাদা রাখা ভালো।
অনুশীলন
-
চিন্তা করুন: আপনার পরিচিত কোনো অ্যাপে এমন একটি UI প্যাটার্ন খুঁজে বের করুন যা স্ক্রিন জুড়ে
বারবার দেখা যায় (যেমন একটি নোটিফিকেশন আইটেম, একটি চ্যাট বাবল, একটি প্রোডাক্ট থাম্বনেইল) — এটিকে কী
প্যারামিটার নিয়ে একটি কাস্টম কম্পোনেন্ট হিসেবে ডিজাইন করবেন?
একটি চ্যাট বাবল কম্পোনেন্টের জন্য যুক্তিসঙ্গত প্যারামিটার হতে পারে
message_text,is_sent_by_me(বাবলের রং/অ্যালাইনমেন্ট নির্ধারণ করতে), ওtimestamp— একইChatBubble()ফাংশন কনভারসেশনের প্রতিটি মেসেজের জন্য একবার করে কল হবে, প্রতিবার ভিন্ন ডেটা দিয়ে, ঠিক এই পাঠেরCard-এর মতোই। -
পরীক্ষা করুন: উপরের কোড সেলে একটি চতুর্থ কার্ড যোগ করুন —
card4 = Card("প্রোডাক্ট: চার্জার", "মূল্য: ৩৫০ টাকা")— এবং সেটিcardsলিস্টে যোগ করুন, তারপর কোড আবার চালিয়ে দেখুন লুপ স্বয়ংক্রিয়ভাবে চতুর্থ কার্ডটিও রেন্ডার করে কি না।হ্যাঁ —
for i, card in enumerate(cards, start=1)লুপটিcardsলিস্টের দৈর্ঘ্যের উপর নির্ভরশীল, নির্দিষ্ট সংখ্যার উপর নয়। তাইcard4যোগ করলে আউটপুটে "-- Card ইনস্ট্যান্স #4 --" স্বয়ংক্রিয়ভাবে দেখা যাবে, এবং "মোট ইনস্ট্যান্স সংখ্যা" ৩ থেকে বেড়ে ৪ হবে — কোনো নতুন রেন্ডারিং কোড না লিখেই, কারণCardওrender()উভয়ই সাধারণীকৃত (generalized)।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ অ্যাপ লাইফসাইকেল, মোবাইল UI/UX, MVVM/MVI আর্কিটেকচার, নেটিভ বনাম ক্রস-প্ল্যাটফর্ম, নেভিগেশন, স্টেট ম্যানেজমেন্ট, লোকাল স্টোরেজ, ডিভাইস ফিচার ও ডিপ্লয়মেন্ট — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
- Full-Stack Web Frameworks কোর্স সহোদর কোর্স ওয়েব কম্পোনেন্ট ফ্রেমওয়ার্কেও (React, Vue) একই "প্যারামিটারাইজড ফাংশন কম্পোজিশন" নীতি প্রয়োগ হয় — শুধু প্ল্যাটফর্ম-নির্দিষ্ট সিনট্যাক্স আলাদা।
- সব 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 ও Mobile App Development — সব এক জায়গায়।