এই কোর্সটা আসলে কী শেখাবে?
বেশিরভাগ React টিউটোরিয়াল আপনাকে শেখায় "কীভাবে" একটা কম্পোনেন্ট লিখতে হয়। কিন্তু রিয়েল ওয়ার্ল্ডে একজন ডেভেলপারের আসল কাজ হলো "কেন" এভাবে বানাতে হয়, সেই সিদ্ধান্তগুলো নেওয়া — কোন ডেটা কোথায় থাকবে, কোন কম্পোনেন্ট কতটুকু দায়িত্ব নেবে, API-এর সাথে ফ্রন্টএন্ড কীভাবে কথা বলবে।
এই কোর্সে আমরা একটা সম্পূর্ণ Social Media Application — নাম দিচ্ছি Connectify — একদম শুরু থেকে বানাবো। কিন্তু লক্ষ্য শুধু "Connectify বানানো" না। লক্ষ্য হলো, এই একটা প্রজেক্ট শেষ করার পর আপনি এমন কিছু reusable engineering pattern রপ্ত করে ফেলবেন, যেগুলো দিয়ে আপনি একটা E-commerce সাইট, একটা Job Portal, একটা Blogging Platform — যেকোনো কিছু বানাতে পারবেন। কারণ প্যাটার্নগুলো একই থাকে, শুধু ডোমেইন বদলায়।
প্রি-রিকুইজিট: JSX, Props,
useState, বেসিকuseEffect— এই জিনিসগুলোর সাথে পরিচিত থাকতে হবে। যদি এগুলো নিয়ে সংশয় থাকে, আগে React-এর বেসিক কনসেপ্টগুলো একবার ঝালিয়ে নিন। এই কোর্স ধরে নিচ্ছে আপনি "React কী" জানেন, কিন্তু "React দিয়ে একটা রিয়েল অ্যাপ কীভাবে আর্কিটেক্ট করতে হয়" — সেটা জানেন না।
এই কোর্সে কী কী থাকবে (পুরো রোডম্যাপ)
কোর্সটা অনেকগুলো পার্টে ভাগ করা থাকবে, প্রতিটা পার্ট আগেরটার ভিত্তির উপর দাঁড়াবে:
| পার্ট | বিষয় | মূল শেখার বিষয় |
|---|---|---|
| ১ | Architecture ও Planning | Requirement Analysis, Data Modeling, API Contract Design, State Ownership |
| ২ | Environment ও Project Bootstrap | Vite, Feature-based Folder Structure, Path Aliases, Environment Variables |
| ৩ | Mock Backend ও Axios Setup | json-server দিয়ে reproducible API, Axios Instance Configuration |
| ৪ | Authentication Architecture | Context API + useReducer দিয়ে Auth State ডিজাইন |
| ৫ | JWT ও Login Flow | Access Token, Refresh Token, Custom useAuth Hook |
| ৬ | Protected Routing | React Router দিয়ে Private/Public Route, Session Persistence |
| ৭ | Axios Interceptors | Auto Token Attach, Auto Refresh, Request Retry Pattern |
| ৮ | Feed ও Posts State | Context + Reducer দিয়ে CRUD Architecture, Optimistic UI |
| ৯ | Custom Hooks Deep Dive | useFetch, useForm, useDebounce, usePagination |
| ১০ | Reusable UI Components | Design System বানানো — Button, Avatar, Modal, Skeleton |
| ১১ | Profile Module | Reducer-based Profile State, Image Upload, Bio Edit |
| ১২ | Utility Functions | timeAgo, Number Formatting, Validators, cn() Helper |
| ১৩ | Error Handling ও Loading States | Error Boundary, Toast System, Empty/Loading States |
| ১৪ | Performance ও Deployment | Code Splitting, Memoization Strategy, Production Build |
এখন চলুন, পার্ট ১ শুরু করি — কোনো কোড লেখার আগে, একজন ইঞ্জিনিয়ারের মতো চিন্তা করা শিখি।
১. কেন কোড লেখার আগে প্ল্যানিং জরুরি?
নতুন ডেভেলপাররা যে সবচেয়ে বড় ভুলটা করে, তা হলো — এডিটর খুলে সরাসরি কম্পোনেন্ট লেখা শুরু করে দেওয়া। এতে শুরুর দিকে দ্রুত প্রগ্রেস মনে হয়, কিন্তু প্রজেক্ট যখন ৫-৬টা ফিচারে পৌঁছায়, তখন দেখা যায়:
- একই ডেটা তিনটা আলাদা কম্পোনেন্টে তিনভাবে fetch হচ্ছে।
- State কোথায় রাখা উচিত ছিল, সেটা নিয়ে কনফিউশন — কখনো props দিয়ে অনেক নিচে পাঠাতে হচ্ছে (prop drilling), কখনো একই ডেটা দুই জায়গায় ডুপ্লিকেট হয়ে আছে।
- একটা ছোট ফিচার যোগ করতে গেলে পাঁচটা ফাইল বদলাতে হচ্ছে।
এই সমস্যাগুলোর মূল কারণ একটাই — আর্কিটেকচার ডিসিশন কোড লেখার সময় নেওয়া হয়েছে, কোড লেখার আগে না। প্রফেশনাল ইঞ্জিনিয়ারিং-এ এই ধাপটাকে বলে System Design বা Technical Planning, আর এটা স্কিপ করলে যা তৈরি হয়, তাকে বলে Technical Debt — মানে ভবিষ্যতে সুদ-সহ শোধ করতে হবে এমন একটা ঋণ।
তাই আমরা কোনো .jsx ফাইল তৈরি করার আগে চারটা প্রশ্নের উত্তর ঠিক করে ফেলব:
- অ্যাপটা আসলে কী কী করবে? (Requirement Analysis)
- ডেটাগুলো দেখতে কেমন হবে? (Data Modeling)
- Frontend আর Backend কীভাবে কথা বলবে? (API Contract)
- কোন ডেটা কোথায় থাকবে? (State Architecture)
২. Requirement Analysis — অ্যাপটা আসলে কী করবে?
Requirement দুই ধরনের হয়, আর দুটোই আলাদাভাবে চিন্তা করা দরকার:
- Functional Requirements — অ্যাপ কী কী কাজ করতে পারবে। যেমন: ইউজার পোস্ট করতে পারবে, লাইক দিতে পারবে।
- Non-Functional Requirements — অ্যাপ কতটা ভালোভাবে কাজ করবে। যেমন: পেজ লোড হতে কত সময় লাগবে, মোবাইলে কেমন দেখাবে, ইউজারের ডেটা কতটা সুরক্ষিত থাকবে।
বেশিরভাগ নতুন ডেভেলপার শুধু প্রথমটা নিয়ে ভাবে, দ্বিতীয়টা ভুলে যায় — ফলে অ্যাপ কাজ করে ঠিকই, কিন্তু স্লো, বা মোবাইলে ভাঙা দেখায়, বা সিকিউরিটি হোল থেকে যায়।
Connectify-এর Functional Requirements
প্রতিটা ফিচারকে আমরা User Story ফরম্যাটে লিখব — "একজন [role] হিসেবে, আমি [action] করতে চাই, যাতে [reason]" — কারণ এই ফরম্যাট শুধু "কী বানাবো" বলে না, "কেন বানাচ্ছি" সেটাও মনে করিয়ে দেয়, যা ডিজাইন সিদ্ধান্তে সাহায্য করে।
| # | User Story | Acceptance Criteria |
|---|---|---|
| 1 | একজন নতুন ভিজিটর হিসেবে, আমি ইমেইল-পাসওয়ার্ড দিয়ে অ্যাকাউন্ট খুলতে চাই | ডুপ্লিকেট ইমেইলে রেজিস্ট্রেশন আটকাতে হবে, পাসওয়ার্ড ৬ ক্যারেক্টারের কম হলে এরর দেখাতে হবে |
| 2 | একজন রেজিস্টার্ড ইউজার হিসেবে, আমি লগইন করতে চাই | ভুল ক্রেডেনশিয়ালে স্পষ্ট এরর মেসেজ দেখাতে হবে |
| 3 | একজন লগইন করা ইউজার হিসেবে, আমি প্রোটেক্টেড পেজে ঢুকতে চাই, লগইন ছাড়া না | লগইন ছাড়া /feed-এ গেলে /login-এ রিডাইরেক্ট হতে হবে |
| 4 | আমি একটা টেক্সট পোস্ট করতে চাই | খালি পোস্ট সাবমিট করা যাবে না |
| 5 | আমি ফিডে সব পোস্ট রিভার্স-ক্রনোলজিক্যাল অর্ডারে দেখতে চাই | নতুন পোস্ট সবার উপরে দেখাতে হবে |
| 6 | আমি একটা পোস্টে লাইক দিতে/তুলে নিতে চাই | UI-তে সাথে সাথে বদল দেখতে হবে (নেটওয়ার্ক রেসপন্সের জন্য অপেক্ষা না করে) |
| 7 | আমি একটা পোস্টে কমেন্ট করতে চাই | কমেন্ট সাবমিট হলে সাথে সাথে লিস্টে যোগ হবে |
| 8 | আমি নিজের বা অন্যের প্রোফাইল দেখতে চাই | নাম, ছবি, বায়ো, এবং তার সব পোস্ট দেখাতে হবে |
| 9 | আমি নিজের প্রোফাইল ছবি ও বায়ো আপডেট করতে চাই | ছবি আপলোডের সময় লোডিং স্টেট দেখাতে হবে |
| 10 | আমি লগআউট করতে চাই, এবং সেশন সম্পূর্ণ মুছে যাবে | লগআউটের পর ব্যাক বাটনে প্রোটেক্টেড পেজে ফেরত যাওয়া যাবে না |
লক্ষ করুন — Acceptance Criteria কলামটা "edge case" নিয়ে ভাবতে বাধ্য করছে (খালি পোস্ট, ভুল পাসওয়ার্ড, ডুপ্লিকেট ইমেইল)। এই edge case গুলোই পরে আমাদের error handling আর validation লজিকের ভিত্তি হবে।
Non-Functional Requirements
- Performance: ফিডে হাজার হাজার পোস্ট থাকলেও UI স্মুথ থাকতে হবে (আমরা pagination দিয়ে এটা সমাধান করব, Part 9-এ)।
- Responsiveness: মোবাইল-ফার্স্ট ডিজাইন — যেহেতু বেশিরভাগ ইউজার মোবাইল থেকে ব্যবহার করবে।
- Security: পাসওয়ার্ড কখনো প্লেইন টেক্সটে স্টোর/দেখানো যাবে না, টোকেন এক্সপায়ার হবে নির্দিষ্ট সময় পর।
- Resilience: নেটওয়ার্ক এরর হলে অ্যাপ ক্র্যাশ করবে না, বরং একটা readable error message দেখাবে (Part 13)।
৩. Data Modeling — ডেটাগুলো দেখতে কেমন হবে?
কোড লেখার আগে ডেটার শেপ ঠিক করাটা এক্সট্রিমলি গুরুত্বপূর্ণ, কারণ পুরো UI আসলে ডেটার একটা প্রতিফলন মাত্র। ডেটা মডেল যদি ভুল হয়, UI-ও ভুল হবে।
আমরা তিনটা মূল Entity (সত্তা) নিয়ে কাজ করব — User, Post, আর Comment। প্রতিটা entity-র মধ্যে সম্পর্ক (relationship) বোঝাটা জরুরি:
User (১) ──── লিখেছে ────> (অনেক) Post
Post (১) ──── আছে ────> (অনেক) Comment
User (১) ──── করেছে ────> (অনেক) LikeUser Entity
{
"id": "usr_a1b2c3",
"firstName": "Rakib",
"lastName": "Hasan",
"email": "rakib@example.com",
"avatarUrl": "/uploads/avatars/usr_a1b2c3.jpg",
"bio": "Frontend developer, coffee addict.",
"createdAt": "2026-01-15T10:00:00.000Z"
}লক্ষ করুন, password এখানে নেই। ইউজারকে কখনো পাসওয়ার্ড ফেরত পাঠানো হয় না — এটা একটা বেসিক সিকিউরিটি প্র্যাকটিস, যাকে বলে data exposure minimization: ক্লায়েন্টকে শুধু ততটুকুই পাঠান, যতটুকু দরকার।
Post Entity
{
"id": "post_x9y8z7",
"authorId": "usr_a1b2c3",
"body": "React শেখা শুরু করলাম আজ থেকে! 🚀",
"imageUrl": null,
"likedBy": ["usr_d4e5f6", "usr_g7h8i9"],
"commentCount": 3,
"createdAt": "2026-02-01T08:30:00.000Z"
}এখানে দুইটা গুরুত্বপূর্ণ ডিজাইন সিদ্ধান্ত আছে, যেগুলো বুঝে নেওয়া দরকার:
প্রথম সিদ্ধান্ত — likedBy একটা array কেন, শুধু একটা likeCount নাম্বার কেন না?
যদি আমরা শুধু likeCount: 42 রাখি, তাহলে ফ্রন্টএন্ডে এটা বোঝার উপায় নেই যে বর্তমান লগইন করা ইউজার নিজে লাইক দিয়েছে কিনা — অথচ Like বাটনটা active/inactive দেখাতে ঠিক এই তথ্যটাই দরকার। তাই likedBy আইডি-দের একটা array রাখলে, ফ্রন্টএন্ডে সহজেই চেক করা যায়:
const hasLiked = post.likedBy.includes(currentUser.id);আর likeCount চাইলে post.likedBy.length থেকেই বের করা যায় — আলাদা করে সংরক্ষণ করার দরকার নেই। এটাই Derived Data-র উদাহরণ, যেটা নিয়ে আমরা পরের সেকশনে আরও গভীরে যাব।
দ্বিতীয় সিদ্ধান্ত — Comment-কে আলাদা commentCount হিসেবে রাখা হয়েছে, কিন্তু পুরো Comment array embed করা হয়নি কেন?
এটাকে বলে Normalization vs Denormalization ট্রেড-অফ। যদি প্রতিটা Post-এর ভেতর তার সব Comment embed করে রাখি, তাহলে ফিড লোড করার সময় (যেখানে হয়তো ২০টা পোস্ট দেখানো হচ্ছে, কমেন্ট দেখানো হচ্ছে না) — অপ্রয়োজনীয়ভাবে অনেক বেশি ডেটা লোড হয়ে যাবে। তাই আমরা শুধু commentCount (একটা সংখ্যা, দেখানোর জন্য যথেষ্ট) রাখি ফিডে, আর আসল Comment গুলো আলাদাভাবে fetch করি, শুধু তখনই যখন ইউজার একটা নির্দিষ্ট পোস্টের কমেন্ট সেকশন খোলে। এটাকে বলে lazy loading — দরকার হওয়ার আগ পর্যন্ত ডেটা না আনা।
Comment Entity
{
"id": "cmt_m3n4o5",
"postId": "post_x9y8z7",
"authorId": "usr_d4e5f6",
"text": "দারুণ শুরু! চালিয়ে যাও।",
"createdAt": "2026-02-01T09:15:00.000Z"
}এখানে postId রাখা হয়েছে যাতে Comment-কে তার Post-এর সাথে যুক্ত করা যায় — একে বলে Foreign Key রেফারেন্স, ঠিক যেভাবে রিলেশনাল ডেটাবেজে করা হয়।
৪. API Contract Design — Frontend আর Backend কীভাবে কথা বলবে?
API Contract মানে হলো একটা লিখিত চুক্তি — কোন endpoint-এ কী পাঠালে কী ফেরত পাওয়া যাবে, সেটার স্পষ্ট ডকুমেন্টেশন। এটা আগে থেকে ঠিক করে রাখলে, Frontend আর Backend আলাদা আলাদাভাবে কাজ করলেও একে অপরের সাথে ঠিকভাবে মিলে যাবে।
আমরা REST আর্কিটেকচারাল স্টাইল অনুসরণ করব। REST-এর মূল নিয়মটা মনে রাখা সহজ:
URL সবসময় একটা "resource" (noun/বিশেষ্য) বোঝাবে, আর HTTP Method বোঝাবে "action" (verb/ক্রিয়া)।
অনেক নতুন ডেভেলপার এই নিয়মটা ভেঙে ফেলে, যেমন POST /posts/likeAPost বা GET /getUserProfile — এগুলো ভুল, কারণ URL-এর ভেতরেই action (verb) ঢুকিয়ে দেওয়া হয়েছে। সঠিক পদ্ধতি হলো resource-টাকে noun রাখা, আর action বোঝানো HTTP method দিয়ে:
| HTTP Method | অর্থ | Idempotent? |
|---|---|---|
GET | ডেটা পড়া (read-only) | হ্যাঁ |
POST | নতুন কিছু তৈরি করা | না |
PATCH | বিদ্যমান কিছুর আংশিক পরিবর্তন | হ্যাঁ (সাধারণত) |
DELETE | মুছে ফেলা | হ্যাঁ |
(Idempotent মানে — একই রিকোয়েস্ট বারবার পাঠালেও ফলাফল একই থাকবে, সিস্টেমে বাড়তি কোনো পরিবর্তন হবে না। যেমন একটা পোস্ট দুইবার DELETE করলে দ্বিতীয়বার শুধু "already deleted" এরর আসবে, নতুন কোনো সাইড-ইফেক্ট হবে না। কিন্তু POST idempotent না — একই রিকোয়েস্ট দুইবার পাঠালে দুইটা আলাদা রিসোর্স তৈরি হয়ে যাবে।)
Connectify API Contract
Base URL: /api
── Authentication ──────────────────────────
POST /auth/register নতুন অ্যাকাউন্ট তৈরি
POST /auth/login লগইন, access + refresh token রিটার্ন করে
POST /auth/refresh এক্সপায়ার্ড টোকেন রিফ্রেশ
── Posts ────────────────────────────────────
GET /posts?page=1&limit=10 পেজিনেটেড ফিড
GET /posts/:postId একটা নির্দিষ্ট পোস্ট
POST /posts নতুন পোস্ট তৈরি
PATCH /posts/:postId পোস্ট এডিট
DELETE /posts/:postId পোস্ট ডিলিট
POST /posts/:postId/likes লাইক টগল করা
GET /posts/:postId/comments একটা পোস্টের সব কমেন্ট
POST /posts/:postId/comments নতুন কমেন্ট যোগ করা
── Users ─────────────────────────────────────
GET /users/:userId প্রোফাইল ডিটেইলস
PATCH /users/:userId প্রোফাইল আপডেট (নাম, বায়ো)
POST /users/:userId/avatar প্রোফাইল ছবি আপলোডলক্ষ করুন — কমেন্ট যোগ করার জন্য আমরা POST /posts/:postId/comments ব্যবহার করছি, PATCH /posts/:postId/comment না। কারণ কমেন্ট একটা নতুন resource তৈরি করার কাজ (একটা নতুন Comment বানানো হচ্ছে), পোস্টকে "আংশিক পরিবর্তন" করা না। এটাই সঠিক REST semantics — এবং এই ছোট্ট সিদ্ধান্তটাই একটা ভালো Backend Engineer আর একজন নতুন ডেভেলপারের মধ্যে পার্থক্য গড়ে দেয়।
HTTP Status Code Convention
প্রতিটা রেসপন্সের সাথে একটা status code আসবে — এগুলো ফ্রন্টএন্ডে error handling লজিক লেখার সময় কাজে লাগবে:
| Code | অর্থ | কখন আসবে |
|---|---|---|
200 OK | সফল রিকোয়েস্ট | সাধারণ GET/PATCH/DELETE সফল হলে |
201 Created | নতুন রিসোর্স তৈরি হয়েছে | POST দিয়ে নতুন পোস্ট/ইউজার তৈরি হলে |
400 Bad Request | রিকোয়েস্ট বডি ভুল/অসম্পূর্ণ | খালি পোস্ট বডি পাঠালে |
401 Unauthorized | টোকেন নেই বা এক্সপায়ার্ড | Protected endpoint-এ টোকেন ছাড়া রিকোয়েস্ট |
403 Forbidden | টোকেন ভ্যালিড, কিন্তু পারমিশন নেই | অন্যের পোস্ট ডিলিট করার চেষ্টা |
404 Not Found | রিসোর্স খুঁজে পাওয়া যায়নি | অস্তিত্বহীন postId |
500 Internal Server Error | সার্ভার সাইড বাগ | Backend-এর সমস্যা |
এই টেবিলটা Part 7-এ Axios Interceptor বানানোর সময় খুব দরকার হবে — যেখানে আমরা 401 পেলে অটোমেটিক টোকেন রিফ্রেশ করব, আর বাকি এররগুলো ইউজারকে দেখাবো।
৫. State Architecture — কোন ডেটা কোথায় থাকবে?
এটাই সম্ভবত পুরো কোর্সের সবচেয়ে গুরুত্বপূর্ণ সিদ্ধান্ত। বেশিরভাগ React অ্যাপের জটিলতা আসে এই একটা প্রশ্নের ভুল উত্তর থেকে: "এই ডেটাটা কে ধরে রাখবে?"
আমরা State-কে চারটা ক্যাটাগরিতে ভাগ করব — এই ভাগগুলো বোঝা মানে React-এর অর্ধেক মাস্টারি অর্জন করে ফেলা।
ক) Local (Component) State
যে ডেটা শুধু একটা কম্পোনেন্টেই দরকার, বাইরের কেউ জানারও দরকার নেই। যেমন — একটা ড্রপডাউন খোলা আছে কিনা, একটা ইনপুট ফিল্ডের বর্তমান ভ্যালু, একটা মডাল ভিজিবল কিনা। এগুলো useState দিয়ে সামলাতে হবে, আর কোনো গ্লোবাল সিস্টেমে নেওয়ার দরকার নেই।
খ) Derived (Computed) State
যে ভ্যালুটা অন্য state থেকে হিসাব করে বের করা যায়, সেটাকে কখনোই আলাদা state হিসেবে সংরক্ষণ করা উচিত না। উপরে আমরা দেখেছি — likeCount-কে আলাদা রাখার বদলে post.likedBy.length থেকে বের করা হচ্ছে। এই নিয়মটা এতটাই গুরুত্বপূর্ণ যে React ডকুমেন্টেশনেও এটাকে "Single Source of Truth" বলা হয় — একটা তথ্যের জন্য একটাই জায়গা থাকবে, বাকি সব জায়গায় সেটা থেকে হিসাব করে নেওয়া হবে। এতে দুইটা কপি "sync" রাখার ঝামেলা থাকে না, বাগও কম হয়।
গ) Global/Shared (Client) State
যে ডেটা একাধিক, দূরে দূরে অবস্থিত কম্পোনেন্ট শেয়ার করে — যেমন লগইন করা ইউজারের তথ্য (Navbar-এও দরকার, ProfilePage-এও দরকার, PrivateRoute-এও দরকার)। এই ডেটা props দিয়ে বারবার নিচে পাঠাতে গেলে (Prop Drilling) কোড অগোছালো হয়ে যায়। এই সমস্যার সমাধান Context API + useReducer — যেটা আমরা Part 4 থেকে ব্যবহার করা শুরু করব।
Redux বা Zustand ব্যবহার করছি না কেন? — এই সাইজের অ্যাপে Context +
useReducerকম্বোই যথেষ্ট। Redux বাড়তি বয়লারপ্লেট আনে, আর এক্সট্রা লাইব্রেরি ডিপেন্ডেন্সি বাড়ায়। React-এর নিজস্ব টুল দিয়েই এই একই প্যাটার্ন শেখা যায় — আর একবার এই প্যাটার্ন বুঝে গেলে, পরে দরকার হলে Redux Toolkit বা Zustand-এ মাইগ্রেট করাটা কঠিন কিছু না, কারণ মূল আইডিয়া একই: state + action + reducer function।
ঘ) Server (Remote) State
এটা এমন এক ধরনের state, যেটা নতুন ডেভেলপাররা প্রায়ই client state-এর সাথে গুলিয়ে ফেলে — অথচ দুটো সম্পূর্ণ আলাদা জিনিস। Server state হলো এমন ডেটা যা আপনার কম্পিউটারে না, ব্যাকএন্ডে বসবাস করে — আমরা শুধু একটা "কপি" fetch করে নিয়ে আসি। যেমন — পোস্ট ফিড, ইউজার প্রোফাইল।
Server state-এর তিনটা বিশেষ বৈশিষ্ট্য আছে, যা client state-এ থাকে না:
- এটা stale (পুরনো) হয়ে যেতে পারে — অন্য কেউ backend-এ ডেটা বদলে ফেললে, আপনার fetch করা কপিটা আর সঠিক থাকে না।
- এটা asynchronously আসে — fetch করতে সময় লাগে, error হতে পারে।
- এটা cache করা যায় — বারবার একই ডেটা re-fetch না করে, আগেরটা সাময়িকভাবে ব্যবহার করা যায়।
এই কোর্সে আমরা Server state ম্যানুয়ালি Context + useReducer দিয়েই সামলাবো (শেখার উদ্দেশ্যে, যাতে ভেতরের মেকানিজম বোঝা যায়)। তবে এটা জেনে রাখা ভালো — প্রোডাকশন প্রজেক্টে এই কাজটার জন্য TanStack Query (React Query) বা SWR-এর মতো লাইব্রেরি ব্যবহার করা হয়, যেগুলো caching, re-fetching, আর stale data — এসব অটোমেটিক্যালি সামলে দেয়। আমরা ম্যানুয়ালি বানিয়ে শিখব বলেই, পরে এই লাইব্রেরিগুলো ব্যবহার করার সময় বুঝবেন এগুলো ভেতরে আসলে কী করছে।
State Ownership Map — Connectify-এর জন্য
| ডেটা | State-এর ধরন | কোথায় থাকবে |
|---|---|---|
| লগইন ফর্মের ইনপুট ভ্যালু | Local | LoginForm কম্পোনেন্টের useState |
| মডাল খোলা/বন্ধ | Local | যে কম্পোনেন্টে মডাল আছে, সেখানেই |
| মোট লাইক সংখ্যা | Derived | post.likedBy.length থেকে হিসাব |
| লগইন করা ইউজার | Global (Client) | AuthContext + useReducer |
| ফিডের পোস্টগুলো | Global (Server) | PostsContext + useReducer |
| প্রোফাইল পেজের ডেটা | Global (Server) | ProfileContext + useReducer |
এই টেবিলটাই আসলে পরের কয়েকটা পার্টের রোডম্যাপ। প্রতিটা row একেকটা Context/Provider হয়ে উঠবে কোডে।
৬. Component Architecture — UI-কে কীভাবে ভাঙব?
ডেটা মডেল ঠিক হয়ে গেলে, এখন চিন্তা করি UI কীভাবে গঠিত হবে। আমরা একটা জনপ্রিয় মেন্টাল মডেল ব্যবহার করব, যাকে বলে Atomic Design — UI-কে জটিলতার লেভেল অনুযায়ী স্তরে ভাগ করা:
- Atoms — সবচেয়ে ছোট, একক-উদ্দেশ্যের উপাদান।
Button,Avatar,Input,Badge। - Molecules — কয়েকটা Atom মিলে একটা ছোট, অর্থবহ ইউনিট।
PostCard(Avatar + টেক্সট + Button মিলে),CommentItem। - Organisms — কয়েকটা Molecule মিলে একটা বড়, স্বয়ংসম্পূর্ণ সেকশন।
Feed(অনেকগুলোPostCard),Navbar,ProfileHeader। - Pages — Organism গুলো মিলে একটা সম্পূর্ণ পেজ।
HomePage,ProfilePage,LoginPage।
এই মডেলের সবচেয়ে বড় সুবিধা — Single Responsibility Principle স্বাভাবিকভাবেই মেনে চলা হয়। প্রতিটা লেভেলের কম্পোনেন্ট একটাই কাজ ভালোভাবে করে, আর উপরের লেভেল নিচের লেভেলকে "কম্পোজ" (compose) করে।
HomePage
└── Feed (organism)
└── PostCard (molecule) ×N
├── Avatar (atom)
├── LikeButton (atom)
└── CommentSection (molecule)
└── CommentItem (atom) ×N৭. পুরো প্রজেক্টের Folder Architecture (প্রিভিউ)
এই সিদ্ধান্তগুলোর ভিত্তিতে, Part 2-এ আমরা যে ফোল্ডার স্ট্রাকচার বানাবো, তার একটা প্রিভিউ দেখে নিই:
connectify/
├── src/
│ ├── app/ # রুট প্রোভাইডার, রাউট কনফিগারেশন
│ │ ├── App.jsx
│ │ └── routes.jsx
│ │
│ ├── features/ # ফিচার-ভিত্তিক মডিউল
│ │ ├── auth/
│ │ │ ├── context/
│ │ │ ├── hooks/
│ │ │ └── components/
│ │ ├── posts/
│ │ │ ├── context/
│ │ │ ├── hooks/
│ │ │ └── components/
│ │ └── profile/
│ │ ├── context/
│ │ ├── hooks/
│ │ └── components/
│ │
│ ├── components/ui/ # রিইউজেবল ডিজাইন সিস্টেম (Atom লেভেল)
│ │ ├── Button.jsx
│ │ ├── Avatar.jsx
│ │ └── Modal.jsx
│ │
│ ├── hooks/ # গ্লোবালি রিইউজেবল কাস্টম হুক
│ ├── lib/ # axios instance, constants
│ ├── utils/ # pure utility functions (timeAgo ইত্যাদি)
│ └── pages/ # টপ-লেভেল পেজ কম্পোনেন্টএখানে "feature-based" ফোল্ডার স্ট্রাকচার ব্যবহার করা হয়েছে, "type-based" স্ট্রাকচার না। পার্থক্যটা বোঝা জরুরি:
- Type-based (পুরনো পদ্ধতি):
components/,hooks/,context/— সব ফাইলের ধরন অনুযায়ী আলাদা ফোল্ডার। সমস্যা হলো, "auth" সম্পর্কিত ফাইলগুলো তিনটা ভিন্ন ফোল্ডারে ছড়িয়ে থাকে। - Feature-based (আমরা যা ব্যবহার করব):
features/auth/,features/posts/— প্রতিটা ফিচারের সব ফাইল (component, hook, context) এক জায়গায়। একটা ফিচার নিয়ে কাজ করতে গেলে একটাই ফোল্ডারে থাকলেই হয়।
Feature-based স্ট্রাকচার প্রজেক্ট বড় হওয়ার সাথে সাথে স্কেল করে ভালো — কারণ নতুন একটা ফিচার (ধরুন, notifications) যোগ করতে হলে, শুধু features/notifications/ নামে একটা নতুন ফোল্ডার বানালেই হয়, বাকি কোডে হাত দিতে হয় না।
৮. Tech Stack — কী দিয়ে বানাবো, আর কেন
| টুল | কাজ | কেন এটাই বেছে নিলাম |
|---|---|---|
| React + Vite | UI লাইব্রেরি + বিল্ড টুল | দ্রুত dev server, Hot Module Replacement, প্রায় জিরো-কনফিগ সেটআপ |
| React Router | ক্লায়েন্ট-সাইড রাউটিং | Public/Private রুট আলাদা করা, <Outlet /> দিয়ে nested layout |
Context API + useReducer | গ্লোবাল স্টেট ম্যানেজমেন্ট | বাড়তি ডিপেন্ডেন্সি ছাড়াই predictable state update প্যাটার্ন শেখা |
| Axios | HTTP ক্লায়েন্ট | Interceptor সাপোর্ট — যা native fetch-এ নেই, কিন্তু Auth ফ্লোর জন্য জরুরি |
| Tailwind CSS | স্টাইলিং | ইউটিলিটি-ফার্স্ট, দ্রুত UI বানানো যায়, ডিজাইন সিস্টেমের সাথে ভালো মেলে |
| json-server (Part 3) | Mock Backend | পুরো Backend না বানিয়েও reproducible API নিয়ে কাজ করা যায় |
Axios আর native
fetch-এর পার্থক্য —fetch-এ প্রতিটা রিকোয়েস্টে ম্যানুয়ালি হেডার বসাতে হয়, JSON parse করতে হয় আলাদা করে, আর এরর হ্যান্ডলিং করতে হয় status code চেক করে। Axios এসব ডিফল্টভাবেই করে দেয়, আর সবচেয়ে গুরুত্বপূর্ণ — Interceptor ফিচার দেয়, যা দিয়ে আমরা প্রতিটা আউটগোয়িং রিকোয়েস্টে অটোমেটিক টোকেন জুড়ে দিতে পারব, আর প্রতিটা ইনকামিং রেসপন্সে401চেক করে অটো-রিফ্রেশ করতে পারব (Part 7)।
এই পার্টের সারাংশ
আমরা এখনো একটাও .jsx ফাইল তৈরি করিনি — কিন্তু এখন আমাদের হাতে আছে:
- একটা স্পষ্ট Feature List, Acceptance Criteria সহ
- একটা ডিজাইন করা Data Model (User, Post, Comment)
- একটা সম্পূর্ণ API Contract, সঠিক REST convention মেনে
- একটা State Ownership Map — কোন ডেটা কোথায় থাকবে সেটা আগে থেকেই ঠিক করা
- একটা Component Architecture প্ল্যান (Atomic Design)
- একটা Folder Structure প্রিভিউ, আর Tech Stack-এর প্রতিটা সিদ্ধান্তের পেছনের কারণ
এই ডকুমেন্টটাই এখন থেকে আমাদের "নর্থ স্টার" — পরের প্রতিটা পার্টে আমরা যা কোড লিখব, তার প্রতিটা লাইন এই প্ল্যানের কোনো না কোনো অংশকে বাস্তবায়ন করবে।
পরের পার্টে (Part 2) আমরা Vite দিয়ে প্রজেক্ট বুটস্ট্র্যাপ করব, উপরে দেখানো ফোল্ডার স্ট্রাকচারটা বাস্তবে বানাবো, Path Alias সেটআপ করব (যাতে ../../../ টাইপ deep import লিখতে না হয়), আর Environment Variables ঠিকভাবে ম্যানেজ করা শিখব।