"React এর হাতেখড়ি" সিরিজের সতেরো পর্বে আমরা শিখেছি JSX, Component, Props, useState, Event Handling, Conditional Rendering, List Rendering, Controlled Component, Form Validation, useEffect, Fetch API, Lifting State Up, Router, Styling, আর Custom Hook। এই শেষ পর্বে সবকিছু একসাথে করে বানাব একটা সম্পূর্ণ Product List — সার্চ ও ফিল্টার সহ।
প্রজেক্টের ফিচারগুলো
- প্রোডাক্ট তালিকা (Fetch দিয়ে বা static ডেটা দিয়ে)
- নাম দিয়ে রিয়েল-টাইম সার্চ (Controlled Input)
- ক্যাটাগরি অনুযায়ী ফিল্টার (Lifting State Up)
- লোডিং ও খালি ফলাফলের অবস্থা (Conditional Rendering)
- প্রতিটা প্রোডাক্ট কার্ড আলাদা Component (Props)
- কার্ট কাউন্ট Custom Hook দিয়ে localStorage-এ সেভ
ধাপ ১ — ডেটা ও প্রধান State
প্রথমে প্রোডাক্টের ডেটা আর ফিল্টারিং-এর জন্য দরকারি সব state একসাথে সাজাই।
const products = [
{ id: 1, name: "ওয়্যারলেস মাউস", category: "ইলেকট্রনিক্স", price: 850 },
{ id: 2, name: "নোটবুক", category: "স্টেশনারি", price: 60 },
{ id: 3, name: "ব্লুটুথ স্পিকার", category: "ইলেকট্রনিক্স", price: 1500 },
{ id: 4, name: "কলমের সেট", category: "স্টেশনারি", price: 120 },
{ id: 5, name: "ব্যাকপ্যাক", category: "ফ্যাশন", price: 2200 }
];
function App() {
const [searchQuery, setSearchQuery] = React.useState("");
const [selectedCategory, setSelectedCategory] = React.useState("সব");
// ...
}
ধাপ ২ — ফিল্টার করা ডেটা বের করা
Advanced JS পর্ব ৫-এর filter চেইন করে সার্চ আর ক্যাটাগরি দুটো শর্তই একসাথে প্রয়োগ করা যায় — কোনো নতুন state লাগে না, প্রতিবার render হওয়ার সময়ই হিসাব হয়।
const filteredProducts = products .filter((p) => selectedCategory === "সব" || p.category === selectedCategory) .filter((p) => p.name.toLowerCase().includes(searchQuery.toLowerCase()));
ধাপ ৩ — Custom Hook দিয়ে কার্ট কাউন্ট
পর্ব ১৭-এর useLocalStorage এখানে সরাসরি কাজে লাগবে — কার্টে যোগ করা প্রোডাক্ট রিফ্রেশের পরও থেকে যাবে।
function useLocalStorage(key, defaultValue) {
const [value, setValue] = React.useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : defaultValue;
});
React.useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
ধাপ ৪ — Component স্ট্রাকচার
বড় UI-কে ছোট component-এ ভাগ করা (পর্ব ৩) — এখানে চারটা মূল অংশ:
App
├── SearchBar (Controlled Input, পর্ব ৯)
├── CategoryFilter (Lifting State Up, পর্ব ১৪)
└── ProductGrid
└── ProductCard (Props, পর্ব ৪; List Rendering, পর্ব ৮)
function SearchBar({ value, onChange }) {
return (
<input
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder="প্রোডাক্ট খুঁজো"
/>
);
}
function CategoryFilter({ categories, selected, onSelect }) {
return (
<div>
{categories.map((cat) => (
<button
key={cat}
onClick={() => onSelect(cat)}
style={{ fontWeight: selected === cat ? "bold" : "normal" }}
>
{cat}
</button>
))}
</div>
);
}
function ProductCard({ product, onAddToCart }) {
return (
<div className="product-card">
<h3>{product.name}</h3>
<p>৳{product.price}</p>
<button onClick={() => onAddToCart(product.id)}>কার্টে যোগ করো</button>
</div>
);
}
ধাপ ৫ — সব একসাথে (App কম্পোনেন্ট)
function App() {
const [searchQuery, setSearchQuery] = React.useState("");
const [selectedCategory, setSelectedCategory] = React.useState("সব");
const [cart, setCart] = useLocalStorage("cart", []);
const categories = ["সব", ...new Set(products.map((p) => p.category))]; // Set দিয়ে ইউনিক ক্যাটাগরি
const filteredProducts = products
.filter((p) => selectedCategory === "সব" || p.category === selectedCategory)
.filter((p) => p.name.toLowerCase().includes(searchQuery.toLowerCase()));
function handleAddToCart(productId) {
setCart([...cart, productId]);
}
return (
<div>
<h1>প্রোডাক্ট তালিকা (কার্টে: {cart.length})</h1>
<SearchBar value={searchQuery} onChange={setSearchQuery} />
<CategoryFilter
categories={categories}
selected={selectedCategory}
onSelect={setSelectedCategory}
/>
{filteredProducts.length === 0 ? (
<p>কোনো প্রোডাক্ট পাওয়া যায়নি</p>
) : (
<div className="product-grid">
{filteredProducts.map((product) => (
<ProductCard key={product.id} product={product} onAddToCart={handleAddToCart} />
))}
</div>
)}
</div>
);
}
এখানে searchQuery আর selectedCategory — দুটো state App-এ "তোলা" আছে (Lifting State Up), SearchBar আর CategoryFilter শুধু callback দিয়ে সেগুলো পাল্টাচ্ছে, নিজেরা কিছু জানে না।
লাইভ ডেমো — সম্পূর্ণ প্রোডাক্ট লিস্ট
নিচে সবকিছু একসাথে কাজ করছে — সার্চ করো, ক্যাটাগরি বাছাই করো, কার্টে যোগ করো। কার্ট কাউন্ট localStorage-এ সেভ হচ্ছে, রিফ্রেশ করলেও থাকবে।
কোড কীভাবে সিরিজের সব শেখা মেলাচ্ছে — সংক্ষেপে
| প্রজেক্টের অংশ | কোন পর্বের কনসেপ্ট |
|---|---|
| SearchBar, ProductCard আলাদা component | পর্ব ৩ (Component) |
{ product, onAddToCart } |
পর্ব ৪ (Props destructuring) |
searchQuery, selectedCategory state |
পর্ব ৫ (useState) |
filteredProducts.length === 0 ? ... : ... |
পর্ব ৭ (Conditional Rendering) |
filteredProducts.map(...) |
পর্ব ৮ (List Rendering) |
value={searchQuery} onChange=... |
পর্ব ৯ (Controlled Component) |
onSelect={setSelectedCategory} |
পর্ব ১৪ (Lifting State Up) |
useLocalStorage("cart", []) |
পর্ব ১৭ (Custom Hook) |
পরবর্তী উন্নতির আইডিয়া
Exercise: প্রজেক্টটা আরও ভালো করার জন্য নিজে চেষ্টা করো —
- পর্ব ১৩-এর টেকনিক দিয়ে
productsstatic অ্যারের বদলে একটা আসল API থেকেuseEffectদিয়ে fetch করো, loading state যোগ করো। - দাম অনুযায়ী কম-বেশি sort করার একটা ড্রপডাউন যোগ করো।
- প্রতিটা প্রোডাক্ট কার্ডে "কার্টে যোগ হয়েছে ✅" — এই ধরনের ভিজ্যুয়াল ফিডব্যাক যোগ করো (হিন্ট: প্রতিটা কার্ডে
cart.includes(product.id)চেক করো)। - পর্ব ১৫-এর React Router দিয়ে প্রতিটা প্রোডাক্টের জন্য একটা আলাদা ডিটেইল পেজ (
/products/:id) বানাও। - পুরো প্রজেক্টটা পর্ব ১১-এর Module প্যাটার্ন দিয়ে আলাদা ফাইলে (
components/,hooks/) ভাগ করো।
সিরিজ শেষ!
"React এর হাতেখড়ি" সিরিজের ১৮ পর্বে আমরা JSX থেকে শুরু করে Component, Props, Hook, Routing, Styling পর্যন্ত সবকিছু শিখেছি, আর আজ সেগুলো একসাথে মিলিয়ে একটা বাস্তব প্রজেক্ট বানিয়েছি। এখন থেকে এই ভিত্তির উপর দাঁড়িয়ে Advanced React (useReducer, Context API, useMemo/useCallback, ইত্যাদি) শেখার সময় এসেছে — অথবা এই মিনি প্রজেক্টটাই নিজের কোনো আসল প্রজেক্টে প্রয়োগ করে দেখো।