এই পর্বে যা শিখবেন
- হাতে DOM মেলানো কেন বড় অ্যাপে ভেঙে পড়ে, আর framework ঠিক কোন সমস্যার সমাধান দেয়
- component, props, event, local ও shared state, derived state, effect, key, controlled form আর routing — সব framework-এর সাধারণ ভাষা
- বদল টের পাওয়ার চারটি পথ চেনা, আর তার দুটি নিজের হাতে বানিয়ে দেখা: re-render (React-এর মতো) আর signal (Solid/Angular-এর মতো) — প্রতিটি প্রায় ২০ লাইনে
- একই “কার্ট কাউন্টার” React, Vue, Svelte আর Angular-এ — কোথায় মিল, কোথায় পার্থক্য
- CSR, SSR, SSG, ISR, streaming, hydration, islands আর React Server Components — আর প্রজেক্ট অনুযায়ী framework বাছাইয়ের যুক্তি
১ · সমস্যাটি আগে — হাতে DOM মেলানো কেন ভেঙে পড়ে
পর্ব ৩৭-এ আপনি DOM নিজের হাতে বদলেছেন: element খুঁজেছেন, textContent বসিয়েছেন, classList বদলেছেন। ছোট পাতায় এটি চমৎকার কাজ করে। এবার একটি অনলাইন দোকানের কথা ভাবুন। একটি পণ্য কার্টে যোগ হলে একসাথে অনেক জায়গা বদলাতে হয়: হেডারের ব্যাজে সংখ্যা, কার্টের তালিকায় নতুন সারি, মোট দাম, “কার্ট খালি” বার্তাটি লুকানো, checkout বোতাম চালু করা, আর পণ্যের কার্ডে “কার্টে আছে” চিহ্ন। পণ্য সরালে আবার সব কয়টি — উল্টো দিকে।
// ✘ হাতে মেলানো: প্রতিটি পরিবর্তনে মনে রাখতে হয় কোন কোন জায়গা বদলাবে
function addToCart(item) {
cart.push(item);
badge.textContent = cart.length; // ১. হেডারের ব্যাজ
list.append(makeRow(item)); // ২. কার্টের তালিকা
totalEl.textContent = sum(cart); // ৩. মোট দাম
emptyMsg.hidden = true; // ৪. "কার্ট খালি" বার্তা
// ৫. checkout বোতাম? ৬. পণ্যের কার্ডের চিহ্ন? — একটি ভুলে গেলেই বাগ
}
function removeFromCart(id) { /* আবার সব কয়টি জায়গা, উল্টো দিকে */ }এই ধরনের কোডকে বলে imperative update — “কী দেখাতে চাই” নয়, “কোন ধাপে কী বদলাব” লেখা। সমস্যা তিনটি। প্রথমত, পরিবর্তনের পথ গুণিতক হারে বাড়ে: n ধরনের ঘটনা আর m জায়গার পাতা মানে প্রায় n × m সংখ্যক হাতে লেখা আপডেট। দ্বিতীয়ত, একটি ভুললেই ডেটা আর পাতা আলাদা কথা বলে — ব্যাজে ৩, তালিকায় ২টি পণ্য। তৃতীয়ত, নতুন একটি জায়গা (যেমন মোবাইলের নিচের বারে কার্টের সংখ্যা) যোগ করলে আগের প্রতিটি আপডেট-পথ খুঁজে খুঁজে বদলাতে হয় — কোড যত বড়, ঝুঁকি তত বেশি।
মিরপুর স্টেডিয়ামের পুরোনো দিনের স্কোরবোর্ড ভাবুন। প্রতিটি রানের পরে একজন কর্মী হাতে টিন বদলাতেন — মোট রান, ব্যাটারের রান, বল, ওভার। একসাথে চারটি বোর্ড বদলাতে গিয়ে একটি ভুললেই দর্শক ভুল স্কোর দেখতেন। আধুনিক ডিজিটাল স্কোরবোর্ড অন্যভাবে চলে: স্কোরার শুধু একটি জিনিস লেখেন — অফিসিয়াল স্কোরশিট (state). সব পর্দা সেই স্কোরশিট থেকে নিজের লেখা নিজে হিসাব করে নেয়। কেউ আর “কোন বোর্ড বদলাতে হবে” মনে রাখে না। Framework ঠিক এই ডিজিটাল বোর্ডের ব্যবস্থা।
তিনটি বড় প্রতিশ্রুতি
পর্ব ২১-এর হিসাব খাতায় আমরা নিয়ম বানিয়েছিলাম “পাতা = f(state)”: ডেটা বদলালে পুরো তালিকা নতুন করে আঁকো। সেটি declarative UI-এর বীজ। Framework এই বীজকে তিনটি প্রতিশ্রুতিতে বড় করে:
🪞 state ↔ DOM সমলয়
আপনি শুধু state বদলান। কোন DOM node বদলাতে হবে, framework বের করে — আর ইনপুটের লেখা, focus, scroll না হারিয়ে শুধু সেটুকুই বদলায়।
🧩 component
পাতাকে ছোট, পুনর্ব্যবহারযোগ্য টুকরোয় ভাগ করা — <ProductCard>, <CartBadge>. প্রতিটির নিজের মার্কআপ, ডেটা আর আচরণ এক জায়গায়।
🏗️ composition
ছোট component জুড়ে বড় component, তারপর পুরো পাতা — যেমন function জুড়ে প্রোগ্রাম। দলের দশজন দশটি component আলাদাভাবে বানাতে পারেন।
মনে রাখুন, framework জাদু নয়। শেষ পর্যন্ত সবাই পর্ব ৩৭-এর createElement, textContent, addEventListener-ই ডাকে। পার্থক্য হলো এই ডাকগুলো আপনি লেখেন না — framework লেখে, আর কম ভুলে লেখে।
২ · সব framework-এর ভাগ করা ভাষা — component, props, state আর effect
React, Vue, Svelte, Angular — নাম আর লেখার ধরন আলাদা, কিন্তু নিচের ধারণাগুলো সবার ভেতরে একই। একটি framework ভালোভাবে শিখলে বাকিগুলো দ্রুত পড়তে পারবেন, কারণ আসলে শুধু শব্দ বদলায়।
| ধারণা | মানে | কার্টের উদাহরণে |
|---|---|---|
| component | একটি function বা ফাইল, যা ডেটা নেয় আর UI-এর একটি টুকরো বর্ণনা করে | <CartBadge>, <ProductCard> |
| props (ডেটা নিচে) | parent থেকে child-এ পাঠানো মান; child এগুলো বদলায় না | <ProductCard product={p} /> |
| event / callback (খবর ওপরে) | child নিজে parent-এর ডেটা বদলায় না; একটি function ডেকে জানায় “এটি ঘটেছে” | onAdd(p.id) |
| local state | শুধু একটি component-এর নিজের মান | ড্রপডাউন খোলা কি না |
| shared state | অনেক component-এর লাগে, তাই ওপরে বা একটি store-এ রাখা | কার্টের পণ্যের তালিকা |
| derived state | অন্য state থেকে হিসাব করা মান — আলাদা করে জমা নয় | মোট দাম, পণ্যের সংখ্যা |
| effect / lifecycle | render-এর বাইরের জগতে কাজ — network, timer, ব্রাউজার API | কার্ট সার্ভারে সেভ করা |
| key | তালিকার প্রতিটি আইটেমের স্থায়ী পরিচয় | key={p.id} |
| routing | URL অনুযায়ী কোন পাতা/component দেখাবে | /cart, /product/7 |
ডেটা নিচে, খবর ওপরে — একমুখী প্রবাহ
সবচেয়ে গুরুত্বপূর্ণ নিয়মটি হলো one-way data flow. ডেটার একটি মালিক থাকে। মালিক ডেটা props হিসেবে নিচে পাঠায়। নিচের component ডেটা নিজে বদলায় না; সে মালিককে একটি callback দিয়ে জানায় — “ব্যবহারকারী চা যোগ করতে চান”। মালিক state বদলায়, আর নতুন ডেটা আবার নিচে নামে। এতে যেকোনো বাগে প্রশ্ন একটি: “এই ডেটার মালিক কে, আর কে তাকে বদলাতে বলেছে?” দুটি sibling component-এর একই ডেটা লাগলে ডেটাকে তাদের সাধারণ parent-এ তুলে নেওয়া হয় — একে বলে lifting state up. অনেক স্তর পেরিয়ে props পাঠাতে হলে (prop drilling) React-এর Context বা একটি store ব্যবহার হয়।
state কম, হিসাব বেশি — derived state
নতুনদের সবচেয়ে চেনা ভুল: cart, count আর total — তিনটিই আলাদা state হিসেবে রাখা। তাহলে তিনটি মিলিয়ে রাখার দায়িত্ব আবার আপনার, অর্থাৎ অংশ ১-এর সমস্যাই ফিরে এল। নিয়ম: যা অন্য state থেকে হিসাব করা যায়, তা state নয় — এটি derived state. শুধু cart রাখুন; count আর total প্রতিবার হিসাব হোক। হিসাব ভারী হলে framework একে cache করার উপায় দেয় — React-এ useMemo, Vue-তে computed, Svelte-এ $derived. এটি পর্ব ২৩-এর pure function আর memoization-এর সরাসরি প্রয়োগ।
effect — render-এর বাইরের জগৎ
Component-এর মূল কাজ ডেটা থেকে UI বর্ণনা করা, আর সেটি pure হওয়া উচিত: একই ডেটা, একই UI. কিন্তু অ্যাপকে বাইরের জগতের সাথেও কথা বলতে হয় — fetch (পর্ব ৩৫), timer, WebSocket, document.title. এই side effect-গুলো রাখা হয় আলাদা জায়গায়, যাকে বলে effect: component পাতায় বসার পরে চলে, নির্ভরতা বদলালে আবার চলে, আর component সরে গেলে cleanup করে (listener সরানো, request বাতিল — পর্ব ৩৬-এর AbortController). পুরোনো class কোডে এদের বলত lifecycle method.
form, routing আর data fetching
Controlled ইনপুটে মান থাকে state-এ: প্রতিটি টাইপে state বদলায়, আর state-ই ইনপুটে মান বসায়। এতে যাচাই (পর্ব ৩৯) সহজ হয়। Routing-এ দুই ঘরানা: পুরোনো MPA (multi-page app) — প্রতিটি লিংকে সার্ভার থেকে নতুন HTML পাতা; আর SPA — একটি পাতা একবার লোড হয়, তারপর JavaScript URL বদলায় (History API) আর শুধু ভেতরের component বদলায়, যাকে বলে client-side routing. Data fetching-এ loading, error, cache, পুরোনো উত্তর আগে আসার race condition — সবই সামলাতে হয়; তাই বড় অ্যাপে এর জন্য আলাদা library (যেমন TanStack Query) জনপ্রিয়।
নিজের হাতে একটি ছোট framework
এবার এই ধারণাগুলো প্রায় ২০ লাইনে বানিয়ে দেখি — কোনো library ছাড়া। একটি createStore state রাখে আর বদলালে subscriber-দের খবর দেয়; একটি component (CartCounter) state নিয়ে DOM ফেরত দেয়; আর render পুরো পাতা নতুন করে বসায়। চালানোর পরে ওপরের ফ্রেমে “+ চা” বোতামটি নিজে চেপে দেখুন:
function createStore(state) { // state রাখে, বদলালে খবর দেয়
const subs = [];
return {
get: () => state,
set(patch) { state = { ...state, ...patch }; subs.forEach((fn) => fn(state)); },
subscribe: (fn) => subs.push(fn),
};
}
const h = (tag, props, ...kids) => { // ছোট্ট element-বানানো helper
const el = Object.assign(document.createElement(tag), props); el.append(...kids); return el;
};
const CartCounter = ({ cart }) => h('div', {}, // component: ডেটা ঢোকে, DOM বের হয়
h('p', {}, `কার্টে ${cart.length}টি — মোট ৳${cart.reduce((s, p) => s + p.price, 0)}`),
h('ul', {}, ...cart.map((p) => h('li', {}, p.name))),
h('button', { onclick: () => store.set({ cart: [...cart, { name: 'চা', price: 20 }] }) }, '+ চা'));
const store = createStore({ cart: [] }), root = document.querySelector('#app'); let renders = 0;
store.subscribe((s) => { renders++; root.replaceChildren(CartCounter(s)); });
root.replaceChildren(CartCounter(store.get())); renders++;
const firstP = root.querySelector('p');
store.set({ cart: [{ name: 'শিঙাড়া', price: 15 }] });
root.querySelector('button').click();
console.log(root.querySelector('p').textContent, '| render:', renders, '| পুরোনো p পাতায় আছে?', firstP.isConnected);আউটপুট কার্টে 2টি — মোট ৳35 | render: 3 | পুরোনো p পাতায় আছে? false. প্রথম render খালি কার্ট দেখিয়েছে; store.set শিঙাড়া যোগ করেছে; কোড নিজে বোতাম চেপে চা যোগ করেছে — প্রতিটি বদলে CartCounter আবার চলেছে, মোট ৩ বার। লক্ষ করুন, কোথাও “ব্যাজ বদলাও”, “তালিকায় সারি যোগ করো” লেখা নেই; component শুধু বলে state থেকে পাতা কেমন হবে। state বদলানোও immutable: [...cart, নতুন] নতুন array বানায় (পর্ব ১৭-এর immutable update). কিন্তু শেষ অংশটি সতর্কবার্তা: false মানে প্রথম <p> আর পাতায় নেই — প্রতিটি render পুরো DOM ফেলে দিয়ে নতুন বানিয়েছে। এখানে একটি ইনপুট থাকলে ব্যবহারকারীর লেখা অর্ধেক কথা আর focus হারিয়ে যেত, আর হাজার সারির তালিকায় এটি ধীর। “সব নতুন করে বর্ণনা করো, কিন্তু DOM-এ শুধু পার্থক্য বসাও” — এই সমস্যার সমাধানই React-এর virtual DOM (অংশ ৩) আর signal (অংশ ৫)।
৩ · React — JSX, hooks আর virtual DOM দিয়ে re-render
Facebook ২০১৩ সালে React open source করে। এর মূল ভাবনা ঠিক ওপরের mini-framework-এর মতো: component হলো একটি সাধারণ JavaScript function, যা state থেকে UI বর্ণনা ফেরত দেয়, আর state বদলালে function-টি আবার চলে। পার্থক্য হলো, React সরাসরি DOM বানায় না — বানায় হালকা object-এর একটি গাছ, virtual DOM, তারপর আগের গাছের সাথে তুলনা করে শুধু পার্থক্যটুকু আসল DOM-এ বসায়। একই কার্ট কাউন্টার React-এ:
import { useState } from 'react';
let nextId = 1;
export function CartCounter() {
const [cart, setCart] = useState([]); // local state
const total = cart.reduce((s, p) => s + p.price, 0); // derived — state নয়, হিসাব
function addTea() {
setCart((prev) => [...prev, { id: nextId++, name: 'চা', price: 20 }]); // নতুন array
}
return (
<div>
<p>কার্টে {cart.length}টি — মোট ৳{total}</p>
<ul>{cart.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
<button onClick={addTea}>+ চা</button>
</div>
);
}
// প্রত্যাশিত আচরণ: শুরুতে "কার্টে 0টি — মোট ৳0"; দুবার চাপলে "কার্টে 2টি — মোট ৳40" আর তালিকায় দুটি "চা"JSX — দেখতে HTML, আসলে JavaScript
Function-এর ভেতরে HTML-এর মতো লেখাটিকে বলে JSX. ব্রাউজার JSX বোঝে না; build tool (পর্ব ৪৬-এর Vite, ভেতরে esbuild বা Babel-এর মতো transpiler) একে সাধারণ function call-এ বদলে দেয়:
// আপনি লেখেন:
<p className="total">মোট ৳{total}</p>
// compiler বানায় (React 17 থেকে চালু "automatic runtime"):
import { jsxs } from 'react/jsx-runtime';
jsxs('p', { className: 'total', children: ['মোট ৳', total] });
// পুরোনো রূপ: React.createElement('p', { className: 'total' }, 'মোট ৳', total)
// ফল একটি সাধারণ object: { type: 'p', props: { className: 'total', children: [...] } }এই একটি সত্য JSX-এর সব “অদ্ভুত” নিয়ম ব্যাখ্যা করে। class JavaScript-এর reserved word, তাই className. {}-এর ভেতরে যেকোনো expression চলে, কিন্তু if বা for statement চলে না — তাই শর্তে ternary বা &&, তালিকায় map (পর্ব ১৬). আর {}-এর ভেতরের লেখা React নিজে escape করে বসায়, তাই ব্যবহারকারীর ডেটা দেখানো স্বাভাবিকভাবেই textContent-এর মতো নিরাপদ। ব্যতিক্রম নামেই বিপদ জানায়: dangerouslySetInnerHTML — পর্ব ৪৩-এর XSS নিয়ম এখানেও খাটে। Vue-র v-html আর Svelte-এর {@html}-ও একই রকম ঝুঁকিপূর্ণ।
hooks — function-এর ভেতরে স্মৃতি
একটি function প্রতিবার নতুন করে চলে, তাহলে cart মনে থাকে কীভাবে? উত্তর hooks (React 16.8, ২০১৯ থেকে). useState মানটি component-এর বাইরে, React-এর নিজের খাতায় রাখে, আর প্রতি render-এ ফেরত দেয়। প্রধান কয়েকটি:
useState— একটি মান আর তাকে বদলানোর function. বদলানোর function ডাকলেই React পরের render-এর সূচি করে। আগের মানের ওপর নির্ভর করলেsetCart(prev => …)রূপ লিখুন।useEffect— render শেষে চলা side effect, সাথে একটি dependency array আর একটি cleanup function.useMemo/useCallback— ভারী হিসাব বা function-কে dependency না বদলানো পর্যন্ত cache করা।useRef— render-এর মাঝে টিকে থাকা একটি বাক্স, যা বদলালে render হয় না (যেমন একটি DOM node বা timer id).
React hooks চেনে ডাকার ক্রম দিয়ে — প্রথম useState মানে খাতার প্রথম ঘর, দ্বিতীয়টি দ্বিতীয় ঘর। তাই দুটি rules of hooks: (১) hook শুধু component-এর ওপরের স্তরে ডাকুন — if, loop বা nested function-এর ভেতরে নয়, যাতে প্রতি render-এ ক্রম একই থাকে; (২) hook শুধু React component বা নিজের বানানো hook (use দিয়ে শুরু নাম) থেকে ডাকুন। ESLint-এর eslint-plugin-react-hooks এগুলো ধরিয়ে দেয়। আরেকটি ফাঁদ পর্ব ২২-এর stale closure: পুরোনো render-এর timer পুরোনো মান দেখে।
useEffect সব কিছুর জায়গা নয়। React-এর অফিসিয়াল ডকুমেন্টেশনে (react.dev) “You Might Not Need an Effect” নামে পুরো একটি অধ্যায় আছে। derived ডেটা (filter করা তালিকা, মোট দাম) effect-এ হিসাব করে আলাদা state-এ রাখলে একটি বাড়তি render আর মেলানোর বাগ — render-এর সময় সরাসরি হিসাব করুন। ব্যবহারকারীর ক্লিকের ফলে কাজ (যেমন অর্ডার পাঠানো) event handler-এ রাখুন, effect-এ নয়। effect মূলত বাইরের system-এর সাথে সমলয় রাখার জন্য — WebSocket সংযোগ, ব্রাউজার API, অন্য library.
একটি ক্লিক থেকে DOM পর্যন্ত — React-এর render চক্র
- event
setCart(…)state বদলের অনুরোধ - render
CartCounter()function আবার চলে - নতুন গাছ
virtual DOMসাধারণ object - diff
reconciliationআগের গাছের সাথে তুলনা - commit
DOM patchশুধু পার্থক্য
onClick → addTea() → setCart(prev => [...prev, চা]) এখনই DOM বদলায় না; React শুধু নোট রাখে “এই component আবার আঁকতে হবে” একই event-এ কয়েকটি setState হলে সব একসাথে (batching)
CartCounter() // পুরো function, ওপর থেকে নিচে useState → cart = [চা] // React-এর খাতা থেকে নতুন মান total = 20 // derived — আবার হিসাব child component-গুলোও (ডিফল্টে) আবার চলে
{ type: 'div', children: [
{ type: 'p', children: ['কার্টে ', 1, 'টি — মোট ৳', 20] },
{ type: 'ul', children: [{ type: 'li', key: 1, children: 'চা' }] },
{ type: 'button', props: { onClick: addTea } } ] }
div → একই type → রেখে দাও p → লেখা 0→1, 0→20 → দুটি text বদল ul > li → key 1 নতুন → একটি li তৈরি button → একই → কিছু না
textNode.nodeValue = '1'; textNode2.nodeValue = '20';
ul.append(document.createElement('li')) // "চা"
div, button, ইনপুটের focus — সব অক্ষত
তারপর effect-গুলো চলে (useEffect)
- state বদলের অনুরোধ।
setCartসরাসরি DOM ছোঁয় না। React শুধু চিহ্ন দেয় যে এই component-এর নতুন render দরকার। একই event-এর ভেতরে কয়েকটি set হলে React সেগুলো একসাথে করে (batching) — একবারই render. - component function আবার চলে। পুরো
CartCounterওপর থেকে নিচে চলে;useStateএবার নতুন মান দেয়,totalনতুন করে হিসাব হয়। ডিফল্টে child component-ও আবার চলে। - নতুন virtual গাছ। JSX থেকে তৈরি সাধারণ object-এর গাছ — “এই state-এ পাতা দেখতে এমন”.
- diff (reconciliation). React নতুন আর পুরোনো গাছ পাশাপাশি রেখে মেলায়: একই জায়গায় একই type হলে node রেখে শুধু property বদলায়; type বদলালে পুরো শাখা নতুন করে বানায়; তালিকায় key দিয়ে কে কে একই, তা চেনে (অংশ ৪)।
- commit — ন্যূনতম DOM patch. শুধু দুটি text node আর একটি নতুন li — mini-framework-এর মতো পুরো DOM ফেলে দেওয়া নয়। তাই ইনপুটের লেখা আর focus টিকে থাকে। এরপর effect চলে। দাম: প্রতিবার পুরো function আর পুরো তুলনা — একটি সংখ্যা বদলালেও।
React Compiler আর ecosystem
বড় React অ্যাপে অপ্রয়োজনীয় re-render কমাতে ডেভেলপাররা বছরের পর বছর হাতে useMemo, useCallback আর memo লিখেছেন — আর প্রায়ই ভুল জায়গায়। React দল এর উত্তর হিসেবে React Compiler এনেছে: build-এর সময় component বিশ্লেষণ করে নিজে থেকে memoization বসায়। ২০২৪ সালে এটি পরীক্ষামূলক (beta) হিসেবে আসে আর ২০২৫ সালে স্থিতিশীল সংস্করণ পায়। তবে এটি শুধু React-এর নিয়ম মানা কোডেই কাজ করে, তাই নিয়ম জানা এখনো জরুরি।
React নিজে শুধু UI library; বাকিটা ecosystem থেকে আসে। Routing-এ React Router, server data-র cache আর loading/error সামলাতে TanStack Query, shared state-এ Redux Toolkit (পর্ব ২৩-এর reducer ধারণা, Redux-এর আধুনিক রূপ) বা ছোট ও সহজ Zustand. মোবাইলে একই ধারণা React Native-এ। নতুন প্রজেক্ট শুরুর পুরোনো টুল Create React App ২০২৫ সালে আনুষ্ঠানিকভাবে deprecated হয়েছে; আজকের পরামর্শ Vite (পর্ব ৪৬) বা একটি meta-framework (অংশ ৭)।
৪ · তালিকা, key আর derived তালিকা — পণ্যের লিস্ট আর খোঁজার বক্স
তালিকা আঁকার সময় framework-কে একটি কঠিন প্রশ্নের উত্তর দিতে হয়: আগের render-এর তিনটি আইটেম আর এখনকার চারটি — কোনটি কোনটির সাথে মেলে? নতুন আইটেম কি শুরুতে ঢুকেছে, নাকি সবগুলোর নাম বদলেছে? Framework আইটেমের ভেতরটা পড়ে অনুমান করে না; সে আপনার দেওয়া key দেখে। React-এ key, Vue-তে :key, Svelte-এ (p.id), Angular-এ track — নাম আলাদা, কাজ এক। key হিসেবে array-এর index দিলে কী হয়, দেখুন। প্রতিটি সারিতে একটি পরিমাণের ইনপুট আছে, আর ব্যবহারকারী “চা”-র পাশে 2 লিখেছেন। এবার তালিকার শুরুতে “পানি” যোগ হলো:
1items = [চা, কফি, লাচ্ছি]2items = [পানি, ...items] // শুরুতে নতুন3// ✘ {items.map((p, i) => <Row key={i} … />)}4// ✔ {items.map((p) => <Row key={p.id} … />)}
| key → আগের আইটেম | |
| key → নতুন আইটেম | |
| React-এর সিদ্ধান্ত | |
| DOM কাজ |
- শুরু। তিনটি সারি; প্রতিটি সারির DOM-এ একটি ইনপুট। ব্যবহারকারী চা-র ইনপুটে 2 লিখেছেন — এই মান DOM-এর ইনপুটে (বা সারির নিজের local state-এ) আছে, items array-তে নয়।
- key = index. নতুন তালিকায় key 0 এখন “পানি”. React দেখে key 0 আগেও ছিল, তাই ভাবে “একই সারি, শুধু লেখা বদলেছে” — প্রথম সারির লেখা চা থেকে পানি, দ্বিতীয়টির কফি থেকে চা… আর শেষে key 3-এর জন্য একটি নতুন সারি। অর্থাৎ একটি যোগ করতে চারটি DOM কাজ।
- বাগ। প্রথম সারির DOM রেখে দেওয়া হয়েছে, তাই তার ইনপুটের “2”-ও রয়ে গেছে — এখন “পানি”-র পাশে! sort, filter বা মুছে ফেলাতেও একই বাগ হয়: checkbox-এর টিক, অ্যানিমেশন বা সারির local state ভুল আইটেমে চলে যায়। কোনো error নেই, শুধু ভুল ফল।
- key = id. প্রতিটি আইটেমের স্থায়ী পরিচয় (ডেটাবেসের id) দিলে React দেখে c, k, l আগেও ছিল — সেগুলো ছোঁয় না; শুধু w নতুন, তাই একটি সারি বানিয়ে শুরুতে বসায়। কম কাজ, আর ইনপুটের 2 চা-র সাথেই থাকে। নিয়ম: key স্থায়ী আর অনন্য হবে; index শুধু তখনই চলে, যখন তালিকা কখনো ক্রম বদলায় না আর সারিতে কোনো অবস্থা নেই।
Math.random()key কখনো নয় — প্রতি render-এ সব সারি নতুন হয়ে যায়।
খোঁজার বক্স — controlled input আর derived তালিকা
এবার একটি পণ্যের তালিকা আর তার ওপরে একটি খোঁজার বক্স। কোন জিনিস state, আর কোনটি হিসাব? ব্যবহারকারী যা টাইপ করেছেন (query), সেটি state — এটি অন্য কিছু থেকে বের করা যায় না। কিন্তু filter করা তালিকা products আর query থেকে হিসাব করা যায় — তাই সেটি derived:
function ProductSearch({ products }) { // props: ডেটা ওপর থেকে আসে
const [query, setQuery] = useState(''); // state: শুধু ব্যবহারকারীর দেওয়া
const visible = products.filter((p) => p.name.includes(query)); // derived: প্রতি render-এ
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} /> {/* controlled */}
<p>{visible.length}টি পণ্য</p>
<ul>{visible.map((p) => <ProductRow key={p.id} product={p} />)}</ul>
</>
);
}
// ✘ AI-এর কোডে প্রায়ই: derived তালিকাকে আলাদা state + effect দিয়ে মেলানো
// const [visible, setVisible] = useState(products);
// useEffect(() => setVisible(products.filter(…)), [products, query]); // বাড়তি render, বাড়তি বাগপ্রত্যাশিত আচরণ: বক্সে “কা” লিখলে শুধু যেসব নামে “কা” আছে, সেগুলো থাকে, আর সংখ্যাও সাথে সাথে বদলায়। তালিকা কয়েক হাজার হলে useMemo বা পর্ব ৩৬-এর debounce ভাবুন — কিন্তু আগে মাপুন (পর্ব ৫৩)।
৫ · Vue, Svelte আর signal — বদল টের পাওয়ার অন্য পথ
React-এর পথ: “কী বদলেছে জানি না, তাই পুরো component আবার চালাই আর তুলনা করি।” বাকিদের প্রশ্ন: যদি আগেই জানা যেত কোন ডেটা কোন DOM node-এ ব্যবহার হয়েছে, তাহলে তুলনারই দরকার হতো না। এই ধারণার নাম fine-grained reactivity, আর এর মূল একক signal: একটি মানের বাক্স, যে মনে রাখে কে কে তাকে পড়েছে, আর বদলালে শুধু তাদের খবর দেয় — পর্ব ৩৮-এর observer pattern, তবে নিজে থেকে subscribe হয়। ছোট একটি signal ব্যবস্থা নিজে বানিয়ে দেখুন:
let current = null; // এই মুহূর্তে কোন effect চলছে
function signal(value) {
const subs = new Set(); // যারা আমাকে পড়েছে
return {
get() { if (current) subs.add(current); return value; }, // পড়লেই নাম লেখা
set(v) { if (v !== value) { value = v; [...subs].forEach((run) => run()); } },
};
}
function effect(fn) {
const run = () => { const prev = current; current = run; fn(); current = prev; };
run();
}
function computed(fn) { const s = signal(); effect(() => s.set(fn())); return { get: s.get }; }
const qty = signal(1), price = signal(120), buyer = signal('রহিম');
const total = computed(() => { console.log(' ⚙ total হিসাব'); return qty.get() * price.get(); });
const $ = (id) => document.getElementById(id);
effect(() => { $('total').textContent = `৳${total.get()}`; console.log(' ↻ total-এর DOM'); });
effect(() => { $('buyer').textContent = buyer.get(); console.log(' ↻ buyer-এর DOM'); });
console.log('qty = 3'); qty.set(3);
console.log('buyer = করিম'); buyer.set('করিম');
console.log('qty = 3 আবার'); qty.set(3);
console.log($('box').textContent);প্রথম তিন লাইন শুরুর সেটআপ: ⚙ total হিসাব, ↻ total-এর DOM, ↻ buyer-এর DOM — প্রতিটি effect একবার চলে, আর চলার সময় যে signal পড়ে, সেই signal তাকে নিজের তালিকায় লিখে রাখে। এরপর qty = 3-এর নিচে শুধু ⚙ total হিসাব আর ↻ total-এর DOM — buyer-এর effect চলেনি, কারণ সে qty পড়েনি। buyer = করিম-এর নিচে শুধু ↻ buyer-এর DOM. qty = 3 আবার-এর নিচে কিছুই নেই — মান একই, তাই set কাউকে খবর দেয়নি। শেষ লাইন ক্রেতা: করিম · মোট: ৳360. কোনো component আবার চলেনি, কোনো গাছ তুলনা হয়নি; শুধু যে text node-এর ডেটা বদলেছে, সেটি বদলেছে। আসল library-গুলো এর সাথে প্রতিবার নির্ভরতা নতুন করে গোনে, বদলগুলো একসাথে করে (batching) আর হিসাব সঠিক ক্রমে চালায়।
1qty, price, buyer = signal(…)2total = computed(qty × price)3effect: totalText ← total4effect: buyerText ← buyer5qty.set(3)6buyer.set('করিম')7// React-এ: setQty(3) → পুরো component + diff
| qty-র subscriber | |
| total-এর subscriber | |
| buyer-এর subscriber | |
| এই ধাপে যা চলল |
- সেটআপ — পড়লেই নাম লেখা। প্রতিটি computed আর effect একবার চলে। চলার সময় যে signal-এর
get()ডাকে, সেই signal তাকে subscriber হিসেবে লিখে নেয়। কেউ হাতে “আমি qty শুনব” বলেনি — এটিই signal-এর স্বয়ংক্রিয় dependency tracking. ফল একটি গাছ: qty, price → total → totalText; buyer → buyerText. - qty.set(3). qty শুধু নিজের subscriber-কে খবর দেয় — total. total আবার হিসাব হয়: 3 × 120 = 360. buyer বা buyerText-এর সাথে qty-র কোনো তার নেই, তাই তারা জানেই না।
- total বদলাল → totalText. total-এর মান বদলেছে, তাই সে নিজের subscriber totalText effect চালায়, যা একটি text node-এ ৳360 বসায়। পুরো পাতায় কাজ বলতে এটুকুই।
- buyer.set('করিম'). এবার উল্টো: শুধু buyerText চলে; total আবার হিসাব হয় না। ১০০টি component-এর একটি বড় পাতায়ও একটি signal বদলালে কাজের পরিমাণ নির্ভর করে শুধু তার সাথে যুক্ত অংশের ওপর, পাতার আকারের ওপর নয়।
- React-এর সাথে তুলনা। React-এ কোনো খাতা নেই:
setQty(3)হলে পুরো component function আবার চলে, নতুন virtual গাছ তৈরি হয়, পুরোটা তুলনা হয় — শেষে একই একটি text node বদলায়। ফল এক, পথ আলাদা: React-এর মডেল সরল (“সব আবার চালাও”), signal-এর মডেল কম কাজ করে, কিন্তু নিয়ম মানতে হয় (মান পড়তে হয়get()বা.valueদিয়ে, নইলে খাতায় নাম ওঠে না)।
একই কার্ট কাউন্টার — Vue, Svelte আর Angular-এ
Vue (Evan You, ২০১৪) একটি single-file component (.vue) ফাইলে script, HTML-এর মতো template আর style একসাথে রাখে। ref আর reactive মানগুলোকে পর্ব ২৮-এর Proxy (আর getter/setter) দিয়ে মোড়ায়: পড়লে নির্ভরতা নোট হয়, লিখলে নির্ভরশীলরা আবার চলে — অর্থাৎ reactivity. তাই এখানে array সরাসরি push করা চলে। <script setup> হলো Composition API-র সংক্ষিপ্ত রূপ; পুরোনো Vue 2-এর “Options API” (data(), methods) এখনো চলে, তবে নতুন কোডে Composition API প্রচলিত।
<script setup>
import { ref, computed } from 'vue';
let nextId = 1;
const cart = ref([]);
const total = computed(() => cart.value.reduce((s, p) => s + p.price, 0));
function addTea() {
cart.value.push({ id: nextId++, name: 'চা', price: 20 }); // সরাসরি বদল — Proxy টের পায়
}
</script>
<template>
<p>কার্টে {{ cart.length }}টি — মোট ৳{{ total }}</p>
<ul><li v-for="p in cart" :key="p.id">{{ p.name }}</li></ul>
<button @click="addTea">+ চা</button>
</template>প্রত্যাশিত আচরণ React-এর মতোই। Vue ভেতরে virtual DOM রাখে, কিন্তু reactivity জানে কোন component-এর ডেটা বদলেছে, তাই শুধু সেটিই আবার আঁকে।
Svelte (Rich Harris, ২০১৬) একটি compiler: build-এর সময় component পড়ে সরাসরি DOM আপডেটের কোড বানায়, তাই ব্রাউজারে পাঠানো runtime খুব ছোট। ২০২৪ সালের Svelte 5 এনেছে runes — $state, $derived, $effect, $props — যেগুলো ভেতরে signal দিয়ে চলে।
<script>
let nextId = 1;
let cart = $state([]); // reactive state
let total = $derived(cart.reduce((s, p) => s + p.price, 0)); // derived
function addTea() {
cart.push({ id: nextId++, name: 'চা', price: 20 }); // $state-এর array গভীরভাবে reactive
}
</script>
<p>কার্টে {cart.length}টি — মোট ৳{total}</p>
<ul>
{#each cart as p (p.id)}<li>{p.name}</li>{/each}
</ul>
<button onclick={addTea}>+ চা</button>প্রত্যাশিত আচরণ একই। Svelte 4-এর $: আর on:click এখন পুরোনো রীতি।
Angular (Google; ২০১৬ সালে পুরোনো AngularJS-কে পুরো নতুন করে লেখা) একটি পূর্ণ framework: routing, form, HTTP client, testing — সব নিজের ভেতরে, আর TypeScript (পর্ব ৪৭) এখানে কার্যত বাধ্যতামূলক। Angular 16 (২০২৩) থেকে signal এসেছে, আর নতুন সংস্করণে এটিই state-এর প্রধান উপায়।
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-cart-counter',
template: `
<p>কার্টে {{ cart().length }}টি — মোট ৳{{ total() }}</p>
<ul>@for (p of cart(); track p.id) { <li>{{ p.name }}</li> }</ul>
<button (click)="addTea()">+ চা</button>`,
})
export class CartCounterComponent {
private nextId = 1;
cart = signal<{ id: number; name: string; price: number }[]>([]);
total = computed(() => this.cart().reduce((s, p) => s + p.price, 0));
addTea() {
this.cart.update((c) => [...c, { id: this.nextId++, name: 'চা', price: 20 }]);
}
}প্রত্যাশিত আচরণ একই। @Component একটি decorator; template-এর @for … track হলো key-এর Angular রূপ। signal পড়তে function-এর মতো ডাকতে হয় — cart().
চারটি কোড পাশাপাশি রাখলে আসল শিক্ষাটি চোখে পড়ে: state, derived মান, তালিকায় key, event-এ function — চারটি ধারণাই প্রতিটিতে আছে। বদলেছে শুধু লেখার ধরন আর বদল টের পাওয়ার যন্ত্র: React-এ নতুন array, Vue আর Svelte-এ সরাসরি push, Angular-এ update.
৬ · Angular, Solid, Preact, Qwik, Lit — বাকি মানচিত্র
Angular-কে আলাদা করে চেনা দরকার, কারণ এর দর্শন বাকিদের থেকে ভিন্ন। React বলে “আমি শুধু UI, বাকিটা আপনি বেছে নিন”; Angular বলে “সব সিদ্ধান্ত আমি নিয়ে রেখেছি”. ফোল্ডার কাঠামো, CLI, routing, form, HTTP — সবকিছুর একটি অফিসিয়াল উপায়। এর কেন্দ্রে dependency injection: একটি component নিজে new CartService() বানায় না; constructor-এ বা inject(CartService) দিয়ে চায়, আর Angular সঠিক instance দিয়ে দেয়। এতে টেস্টে আসল service-এর জায়গায় নকল দেওয়া সহজ (পর্ব ৪৮)। আগে Angular বদল টের পেত Zone.js দিয়ে পুরো গাছ পরীক্ষা করে; signal আসার পরে সে zoneless দিকে সরছে। বড় দল, দীর্ঘমেয়াদি প্রজেক্ট, ব্যাংক বা সরকারি এন্টারপ্রাইজ সফটওয়্যার — যেখানে “সবাই একই নিয়মে লিখবে” গুরুত্বপূর্ণ, সেখানে Angular প্রায়ই দেখা যায়।
| নাম | মূল ভাবনা | কখন ভাববেন |
|---|---|---|
| SolidJS | JSX লেখা React-এর মতো, কিন্তু component একবারই চলে; signal সরাসরি DOM আপডেট করে | React-এর ধাঁচ চান, গতি আর ছোট আকারও চান |
| Preact | খুব ছোট (কয়েক KB) React-সদৃশ library; নিজস্ব signals package | বিদ্যমান React জ্ঞান, কিন্তু bundle ছোট রাখা জরুরি |
| Qwik | resumability: সার্ভারের অবস্থা HTML-এ লিখে পাঠায়, ব্রাউজারে hydration ছাড়াই “আবার শুরু”; JS নামে শুধু দরকারের মুহূর্তে | খুব দ্রুত প্রথম লোড যেখানে মূল লক্ষ্য |
| Lit | ব্রাউজারের নিজস্ব Web Components (custom element + Shadow DOM) সহজে বানানোর হালকা library | যেকোনো framework-এ চলবে এমন ডিজাইন সিস্টেম |
| htmx / Alpine.js | HTML attribute দিয়ে সার্ভার থেকে HTML টুকরো আনা (htmx) বা ছোট interactivity (Alpine) | সার্ভার-রেন্ডারড সাইটে অল্প প্রাণ যোগ (অংশ ৮) |
মনে রাখুন, Web Components framework-এর বিকল্প নয়, পরিপূরক: নিজের ট্যাগ আর আলাদা style দেয়, কিন্তু state থেকে UI আঁকা বা routing দেয় না।
৭ · Meta-framework আর rendering — CSR, SSR, SSG, hydration আর islands
এ পর্যন্ত সব কোড চলেছে ব্রাউজারে: ফাঁকা একটি HTML আসে, JavaScript নামে, তারপর component পাতা আঁকে। একে বলে CSR (client-side rendering). ড্যাশবোর্ডে এটি ঠিক আছে, কিন্তু একটি পণ্যের পাতা বা সংবাদের পাতায় সমস্যা: ধীর ফোনে কয়েক সেকেন্ড সাদা পাতা, আর search engine বা সোশ্যাল মিডিয়ার preview ঠিকমতো লেখা পায় না। সমাধান হলো সার্ভারেই component চালিয়ে HTML বানানো — আর সেই কাজের জন্য React, Vue বা Svelte-এর ওপরে একটি স্তর লাগে, যাকে বলে meta-framework: Next.js আর React Router 7 (Remix-এর উত্তরসূরি) React-এর জন্য, Nuxt Vue-র জন্য, SvelteKit Svelte-এর জন্য, Angular-এর নিজের SSR, আর Astro যেকোনোটির সাথে।
| ধরন | HTML কখন তৈরি হয় | ভালো যেখানে | দাম |
|---|---|---|---|
| CSR | ব্রাউজারে, JS নামার পরে | লগইনের পেছনের ড্যাশবোর্ড, admin panel | প্রথম লোড ধীর, SEO দুর্বল |
| SSR | সার্ভারে, প্রতিটি অনুরোধে | ব্যক্তিগত বা ঘনঘন বদলানো পাতা (কার্ট, ফিড) | সার্ভার খরচ, প্রতিটি অনুরোধে কাজ |
| SSG | build-এর সময়, একবার | ব্লগ, ডকুমেন্টেশন, মার্কেটিং পাতা | ডেটা বদলালে আবার build |
| ISR | build-এ, তারপর নির্দিষ্ট সময় পরপর পেছনে নতুন করে | হাজার পণ্যের ক্যাটালগ | কিছুক্ষণ পুরোনো পাতা দেখা যেতে পারে |
| Streaming SSR | সার্ভারে, তৈরি অংশ আগে আগে পাঠানো | ধীর ডেটার অংশসহ পাতা | জটিলতা বেশি |
SSR বা SSG-তে ব্রাউজার আগেই সম্পূর্ণ HTML পায়, তাই লেখা দ্রুত দেখা যায় — পর্ব ৫৩-এর Core Web Vitals-এর LCP ভালো হয়। কিন্তু সেই HTML এখনো “মৃত”: বোতামে listener নেই, state নেই। তাকে জীবন্ত করার ধাপটির নাম hydration:
- অনুরোধ
GET /product/7ব্রাউজার থেকে - সার্ভার render
component → HTMLডেটাসহ - দেখা যায়
LCPকিন্তু বোতাম অচল - JS নামে
bundle.jsparse আর চালানো - hydrate
listener + stateএখন interactive
GET /product/7 — CSR হলে উত্তর হতো ফাঁকা <div id="root"></div>
product = await db.find(7) // পর্ব ৫২ html = renderToString(<ProductPage product={product} />) <h1>জামদানি শাড়ি</h1><p>৳4500</p><button>কার্টে যোগ</button> + ডেটার একটি JSON কপি, + <script src="bundle.js">
পর্দায় নাম, দাম, বোতাম — দ্রুত (ভালো LCP) ব্যবহারকারী "কার্টে যোগ" চাপলেন → কিছুই হলো না listener এখনো লাগেনি; ধীর ফোনে এই ফাঁক কয়েক সেকেন্ড
bundle.js নামে → parse → চলে যে অংশ কখনো বদলায় না (শিরোনাম, বর্ণনা), তার কোডও নামে main thread ব্যস্ত → INP খারাপ হতে পারে (পর্ব ৫৩)
hydrateRoot(root, <ProductPage product={json} />)
→ component আবার চলে, কিন্তু DOM নতুন বানায় না
→ বিদ্যমান node-এর সাথে মিলিয়ে listener আর state লাগায়
সার্ভার আর ব্রাউজারের HTML না মিললে "hydration mismatch" সতর্কবার্তা
- অনুরোধ। ব্রাউজার একটি সাধারণ পাতা চায়।
- সার্ভারে render. সার্ভার একই component চালিয়ে ডেটাসহ সম্পূর্ণ HTML বানায়। সাথে ডেটার একটি JSON কপি পাতায় রেখে দেয়, যাতে ব্রাউজারকে আবার fetch করতে না হয়।
- দেখা যায়, কিন্তু অচল। ব্রাউজার HTML পেয়েই আঁকে — নাম, দাম, ছবি দ্রুত দেখা যায়। কিন্তু JavaScript এখনো আসেনি, তাই বোতাম চাপলে কিছু হয় না। দেখতে তৈরি অথচ সাড়া না দেওয়া এই অবস্থাকে অনেকে বলেন uncanny valley — ব্যবহারকারীর কাছে এটি “ভাঙা সাইট”.
- JavaScript নামে। পুরো অ্যাপের bundle নামে আর চলে — যে অংশ কখনো interactive নয়, তার কোডও।
- hydrate. Framework component আবার চালায়, কিন্তু DOM নতুন বানানোর বদলে বিদ্যমান DOM-এর সাথে মিলিয়ে listener আর state লাগায়। এখন পাতা interactive. সার্ভারে আর ব্রাউজারে ভিন্ন HTML হলে (যেমন render-এ
new Date()বাMath.random()) mismatch হয়। এই খরচ কমানোর তিন আধুনিক পথ: islands (শুধু interactive অংশ hydrate), React Server Components (সার্ভার-only component-এর JS পাঠানোই হয় না) আর resumability (Qwik — hydration-ই নেই)।
islands আর React Server Components
Islands architecture-এ পাতা মূলত স্থির HTML-এর একটি সমুদ্র; তার মাঝে কয়েকটি interactive “দ্বীপ” — একটি কার্ট বোতাম, একটি ছবির carousel. শুধু দ্বীপগুলোর JavaScript নামে আর hydrate হয়, প্রতিটি আলাদাভাবে। Astro এই ধারণার সবচেয়ে পরিচিত উদাহরণ, আর এর দ্বীপ React, Vue, Svelte — যেকোনোটি দিয়ে লেখা যায়। লেখা-প্রধান সাইটে এটি পাঠানো JavaScript অনেক কমায়।
React Server Components (RSC) একই সমস্যা React-এর ভেতর থেকে সমাধান করতে চায়। কিছু component শুধু সার্ভারে চলে — সরাসরি ডেটাবেস পড়তে পারে, আর তাদের কোড কখনো ব্রাউজারে যায় না; ব্রাউজারে যায় শুধু ফল। interactive অংশগুলো আলাদা করে 'use client' চিহ্ন দিয়ে “client component” বানানো হয়। Next.js-এর App Router এই মডেলে চলে। এটি শক্তিশালী, কিন্তু কোন কোড কোথায় চলছে আর কোন ডেটা সীমানা পেরোচ্ছে (secret যেন client-এ না যায়), তা নিয়ে সতর্ক থাকতে হয়। পুরোনো Next.js-এর pages/ router আর getServerSideProps এখনো চলে, কিন্তু নতুন ডকুমেন্টেশনের কেন্দ্রে App Router — AI-এর কোডে দুটি মিশে যাওয়া খুব সাধারণ (অংশ ৯)।
৮ · কোনটি বেছে নেবেন — আর কখন কোনোটিই নয়
“সবচেয়ে ভালো framework কোনটি?” প্রশ্নটির সৎ উত্তর: প্রজেক্ট আর দলের ওপর নির্ভর করে। বিচার করার পাঁচটি মাপকাঠি:
- দলের দক্ষতা। দলের জানা framework-এর উৎপাদনশীলতা প্রায়ই নতুনটির সব সুবিধার চেয়ে বেশি।
- অ্যাপের ধরন। বেশিরভাগ লেখা আর অল্প interactivity (ব্লগ, ডকুমেন্টেশন, কোম্পানির সাইট) → Astro বা অন্য SSG. লগইনের পেছনে অনেক ফর্ম আর চার্ট (ড্যাশবোর্ড, ERP) → React বা Vue, প্রায়ই CSR. বড় দল, দীর্ঘমেয়াদি এন্টারপ্রাইজ → Angular-এর কাঠামো সুবিধা দেয়। SEO-নির্ভর ই-কমার্স → Next.js, Nuxt বা SvelteKit-এর SSR/ISR.
- পারফরম্যান্স আর bundle. সস্তা ফোন আর ধীর নেটওয়ার্কে প্রতিটি KB গুরুত্বপূর্ণ। Svelte, Solid, Preact ছোট runtime দেয়; React-এর আকার বড়, কিন্তু SSR/RSC দিয়ে কমানো যায়। অনুমান নয় — পর্ব ৫৩-এর মতো মাপুন।
- শেখার ঢাল। Vue আর Svelte HTML জানা মানুষের কাছে সহজ শুরু; React-এ “এটি শুধু JavaScript”, কিন্তু hooks-এর নিয়ম আর ecosystem-এর অসংখ্য বিকল্প শিখতে হয়; Angular-এ শুরুতে শেখার জিনিস সবচেয়ে বেশি (TypeScript, DI, RxJS).
- চাকরির বাজার। বাংলাদেশে আর বিদেশের remote কাজে front-end চাকরির বিজ্ঞাপনে React সবচেয়ে বেশি চাওয়া হয় — এটি প্রচলিত ধারণা, আর বিজ্ঞাপন দেখলেই মিলবে। Laravel-ভিত্তিক অনেক প্রতিষ্ঠানে Vue, আর কিছু বড় এন্টারপ্রাইজ ও ব্যাংকিং সফটওয়্যারে Angular দেখা যায়। নিজে কয়েকটি job portal-এ খুঁজে সংখ্যাটা যাচাই করুন — অন্যের দাবি নয়।
একটি উদাহরণ — হাসপাতালের ড্যাশবোর্ড
ধরুন, ঢাকার একটি হাসপাতালের জন্য রোগী ভর্তি, বেড আর বিলের একটি অভ্যন্তরীণ ড্যাশবোর্ড বানাবেন। লগইনের পেছনে, SEO লাগে না → CSR যথেষ্ট। অনেক ফর্ম, টেবিল আর live বেড-সংখ্যা → component framework উপকারী। দলের তিনজন React জানেন → React + Vite + TanStack Query + একটি UI component library যুক্তিসংগত পছন্দ। দল Vue জানলে Vue সমান ভালো। আর যদি একই হাসপাতালের পাবলিক ওয়েবসাইট (ডাক্তারের তালিকা, সময়সূচি) বানাতে হয় — সেটি আলাদা প্রজেক্ট: স্থির লেখা আর SEO → Astro বা SSG. একই প্রতিষ্ঠান, দুটি ভিন্ন সঠিক উত্তর।
কখন framework লাগে না
একটি পাঁচ পাতার কোম্পানি সাইট, একটি যোগাযোগ ফর্ম, একটি ছবির gallery — এখানে React মানে build pipeline আর বড় bundle, অথচ দরকার কয়েক লাইনের DOM কোড। পর্ব ৩৭-এর vanilla JavaScript আর progressive enhancement — আগে কাজ করা HTML, তারপর JavaScript দিয়ে উন্নত করা — এখানে যথেষ্ট। সার্ভার-রেন্ডারড অ্যাপে (Laravel, Django, Rails) অল্প প্রাণ যোগ করতে htmx (HTML attribute দিয়ে সার্ভার থেকে HTML টুকরো এনে বসানো) বা Alpine.js (মার্কআপের ভেতরে ছোট reactive আচরণ) আজকাল জনপ্রিয় বিকল্প। ABCL TECH-এর এই কোর্সের পাতাগুলোও কোনো framework ছাড়া, সাধারণ HTML আর অল্প JavaScript-এ বানানো।
শেখার পথ
একটি সৎ পরামর্শ: framework দিয়ে JavaScript শেখা শুরু করবেন না। যিনি closure (পর্ব ২২), this, Promise আর async/await (পর্ব ৩৩–৩৪), DOM আর event (পর্ব ৩৭–৩৮), module (পর্ব ৪৫) বোঝেন, তিনি React-এর stale closure, useEffect-এর cleanup বা hydration-এর বাগ ধরতে পারেন; যিনি বোঝেন না, তিনি শুধু AI-কে বারবার জিজ্ঞেস করেন। এই কোর্স শেষ করে একটি framework বেছে নিন — বাজারের কারণে সাধারণত React, অথবা আপনার দল বা প্রজেক্ট যা চায় — আর তার অফিসিয়াল ডকুমেন্টেশন (react.dev, vuejs.org, svelte.dev, angular.dev) দিয়ে গভীরভাবে শিখুন। সাথে TypeScript (পর্ব ৪৭) আর টেস্টিং (পর্ব ৪৮), আর পর্ব ৫৫-এর মতো একটি সত্যিকারের প্রজেক্ট। দ্বিতীয় framework তখন সহজ, কারণ ধারণাগুলো একই।
Desktop GUI-তে একই বিবর্তন দেখা গেছে। Java Swing বা Python-এর Tkinter-এ আপনি widget বানিয়ে হাতে বদলান — imperative. JavaFX-এর property binding (label.textProperty().bind(…)) আর C++-এর Qt-এর signals ও slots আসলে reactive ধারণা, আর Qt-এর QML পুরোপুরি declarative — এগুলো সবই Observer pattern-এর রূপ (Java-র design pattern পর্ব)। সার্ভারে Django/Jinja বা Java-র Thymeleaf template ডেটা থেকে HTML বানায় — SSR-এর পুরোনো, সরল রূপ (Python-এর ওয়েব ব্যাকএন্ড পর্ব)। মোবাইলে SwiftUI, Jetpack Compose আর Flutter একই “UI = f(state)” দর্শনে চলে (ডিক্লারেটিভ বনাম ইম্পারেটিভ UI).
- Virtual DOM কী, আর এটি কেন? — UI-এর হালকা object-গাছ; নতুন আর পুরোনো গাছ তুলনা করে শুধু পার্থক্য আসল DOM-এ বসানো, যাতে “সব আবার বর্ণনা করো” মডেল দক্ষভাবে চলে।
- তালিকায় key কেন লাগে, index কেন খারাপ? — render-এর মাঝে কোন আইটেম কোনটি, তা চেনাতে; ক্রম বদলালে index-key ভুল সারিতে state/DOM রেখে দেয়।
- props আর state-এর পার্থক্য? — props parent থেকে আসে, child বদলায় না; state component-এর নিজের, বদলালে আবার render.
- SSR আর CSR-এর পার্থক্য, hydration কী? — HTML সার্ভারে বনাম ব্রাউজারে তৈরি; hydration সার্ভারের HTML-এ listener আর state লাগিয়ে interactive করে।
- Rules of hooks কী? — hook শুধু component-এর ওপরের স্তরে আর শুধু React function থেকে ডাকা, কারণ React ডাকার ক্রম দিয়ে hook চেনে।
৯ · AI-এর যুগে — AI-এর লেখা framework কোড যাচাই করা
Framework কোড AI-এর সবচেয়ে দুর্বল জায়গাগুলোর একটি — কারণ এর API দ্রুত বদলায়, আর AI-এর প্রশিক্ষণের ডেটায় দশ বছরের পুরোনো আর নতুন কোড মিশে আছে। ফলে একই উত্তরে React-এর class component আর hooks, Vue 2-এর Options API আর Vue 3-এর <script setup>, Next.js-এর পুরোনো pages router আর নতুন App Router মিশে যায়। সাধারণ ভুলগুলো:
| AI-এর সাধারণ ভুল | ফল | ঠিক রূপ |
|---|---|---|
পুরোনো API মেশানো: class component, componentDidMount, Vue 2-এর data(), getServerSideProps App Router-এ | কাজ করে না, বা দুই যুগের কোড এক প্রজেক্টে | প্রম্পটে সংস্করণ বলুন; package.json দেখুন; অফিসিয়াল ডকুমেন্টেশনের সাথে মেলান |
সব কিছু useEffect-এ — derived ডেটা, ক্লিকের ফল | বাড়তি render, অসীম loop, মেলানোর বাগ | derived মান render-এ হিসাব; ক্লিকের কাজ event handler-এ |
key নেই বা key={index} | ক্রম বদলালে ভুল সারিতে ইনপুট/state | ডেটার স্থায়ী id |
| ছোট জিনিসেও global store (Redux, Context) | অপ্রয়োজনীয় জটিলতা আর re-render | আগে local state, দরকার হলে ওপরে তোলা |
state সরাসরি বদলানো: cart.push(x); setCart(cart) | React একই reference দেখে render করে না | setCart(prev => [...prev, x]) |
| effect-এ fetch, কিন্তু cleanup বা AbortController নেই | পুরোনো উত্তর নতুনটিকে ঢেকে দেয় (race) | cleanup-এ abort, বা TanStack Query-র মতো library |
AI-কে framework কোড লেখাতে চাইলে সংস্করণ আর নিয়ম আগেই বলে দিন:
AI-এর লেখা framework কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১jQuery (২০০৬) থেকে Backbone আর AngularJS (২০১০) হয়ে React (২০১৩) — ওয়েব UI লেখার ধারণা কীভাবে বদলাল, আর কেন two-way binding থেকে one-way data flow-তে সরে আসা হলো?
২০০৬ সালে John Resig-এর jQuery এসে DOM বদলানো সহজ করে — $('.badge').text(3). কিন্তু jQuery-র কোনো মত ছিল না ডেটা কোথায় থাকবে। ফলে বড় অ্যাপে ডেটা থাকত DOM-এর ভেতরেই — একটি data- attribute-এ, একটি লুকানো ইনপুটে, বা একটি বোতামের class-এ — আর শত শত event handler একে অপরের DOM বদলাত।
২০১০ সালে দুটি উত্তর আসে। Jeremy Ashkenas-এর Backbone.js ডেটাকে DOM থেকে আলাদা model-এ রাখল; model বদলালে event ছুড়ত, আর view নিজেকে আঁকত — তবে আঁকার কোড হাতে লিখতে হতো। একই বছর Google-এর Miško Hevery-র হাত ধরে আসা AngularJS আরও সাহসী ছিল: two-way data binding. template-এ ng-model="name" লিখলে ইনপুট বদলালে model বদলায়, model বদলালে ইনপুট — নিজে থেকেই। ডেমোতে এটি ছিল জাদু।
সমস্যা দেখা দিল বড় অ্যাপে। AngularJS বদল টের পেত “dirty checking” দিয়ে: প্রতিটি ঘটনার পরে সব binding-এর মান আগের সাথে মেলানো, আর কিছু বদলালে আবার পুরো চক্র — তাই হাজার binding-এ পাতা ধীর হতো। আরও গভীর সমস্যা ছিল বোঝার: একটি মান বদলালে কে কাকে বদলাল, তার শিকল খুঁজে পাওয়া কঠিন, কারণ ডেটা দুই দিকেই বইতে পারে। Facebook ২০১৪ সালে এমন জট এড়াতে Flux নকশা উপস্থাপন করে।
২০১৩ সালের React এই দিকটি উল্টে দেয়: ডেটা শুধু এক দিকে বয় — state থেকে UI-তে। UI ডেটা বদলাতে চাইলে একটি callback ডাকে, মালিক state বদলায়, আর পুরো UI নতুন করে বর্ণনা হয়। লিখতে একটু বেশি হয়, কিন্তু প্রতিটি পরিবর্তনের একটিই পথ। পরে Redux (২০১৫) এই নীতিকে পুরো অ্যাপে নিয়ে যায়। Vue-র v-model আজও আছে, কিন্তু ভেতরে এটি একমুখী প্রবাহের ওপর বসানো সুবিধা মাত্র: একটি মান নিচে, একটি event ওপরে।
মূল উপলব্ধি: বিবর্তনটি ছিল “কম লেখা” থেকে “সহজে বোঝা”-র দিকে। বড় সফটওয়্যারে লেখার সময় যতটা খরচ, পড়া আর বাগ খোঁজার খরচ তার বহুগুণ — তাই যে নকশা ডেটার পথ স্পষ্ট রাখে, শেষ পর্যন্ত সেটিই টেকে।
প্র ০২JavaScript-এর ভেতরে HTML (JSX) প্রথমে অনেককে রাগিয়ে দিয়েছিল। ২০১৩ সালে Pete Hunt-এর “separation of concerns, not technologies” যুক্তিটি কী ছিল, আর এই বিতর্ক আজও কেন প্রাসঙ্গিক?
২০১৩ সালের মে মাসে JSConf US-এ React প্রথম দেখানোর সময় শ্রোতাদের প্রতিক্রিয়া ছিল মিশ্র, অনেকটাই নেতিবাচক। কারণ JSX দেখতে ছিল ওয়েব ডেভেলপমেন্টের এক দশকের “পবিত্র নিয়ম” ভাঙার মতো। নিয়মটি ছিল separation of concerns: HTML আলাদা ফাইলে (গঠন), CSS আলাদা (চেহারা), JavaScript আলাদা (আচরণ)।
সেই বছরের শেষে JSConf EU-তে Facebook-এর প্রকৌশলী Pete Hunt “React: Rethinking Best Practices” নামে একটি বক্তৃতা দেন, যা বিতর্কের মোড় ঘুরিয়ে দেয়। তাঁর যুক্তি: আলাদা ফাইলে রাখলে আপনি প্রযুক্তি আলাদা করছেন, দায়িত্ব নয়। একটি “কার্ট ব্যাজ”-এর মার্কআপ, ডেটা আর আচরণ একসাথে বদলায় — তিন ফাইলে ছড়ালে একটি বদলাতে তিনটি খুলতে হয়। আর template ভাষা আসলে একটি দুর্বল প্রোগ্রামিং ভাষা, যার loop আর শর্তের নিয়ম আলাদা করে শিখতে হয়। তাহলে পূর্ণ শক্তির JavaScript কেন নয়? দায়িত্ব অনুযায়ী ভাগ করার সঠিক একক হলো component.
যুক্তিটি টিকে গেল। আজ প্রায় সব framework-এ component-ই মূল একক — এমনকি Vue-র single-file component-ও HTML, JS আর CSS-কে এক ফাইলে রাখে, যদিও আলাদা অংশে। তবু বিতর্ক শেষ হয়নি, শুধু নতুন রূপ নিয়েছে। CSS-in-JS আর Tailwind CSS নিয়েও একই “এটি তো inline style!” বনাম “একসাথে বদলায়, তাই একসাথে থাকুক” বিতর্ক। Svelte আর Vue template রাখল, কারণ template compiler-কে বিশ্লেষণের সুযোগ দেয়।
মূল উপলব্ধি: কোড কোন ফাইলে থাকবে, তা ঠিক করা উচিত “কোনটি একসাথে বদলায়” প্রশ্ন দিয়ে, “কোন ভাষায় লেখা” দিয়ে নয়। নিয়মের পেছনের কারণ জানলে নিয়ম ভাঙার সঠিক সময়ও চেনা যায়।
প্র ০৩Svelte বলল framework-এর কাজ compiler করুক; আর ২০১০ সালের Knockout-এর observable “signal” নামে ২০২০-এর দশকে ফিরে এল। এই দুই ধারা কোথা থেকে এল, আর TC39-এর Signals প্রস্তাব কী নির্দেশ করে?
Signal-এর ধারণা নতুন নয়। ২০১০ সালে Steve Sanderson-এর Knockout.js-এ ছিল ko.observable আর computed observable: একটি মান পড়লে নির্ভরতা নিজে থেকে নোট হতো, বদলালে নির্ভরশীল UI আপডেট হতো — এই পর্বের mini-signals-এর প্রায় হুবহু ধারণা। পরে MobX আর Vue-ও এই পথে চলে, কিন্তু React-এর সাফল্যের পরে আলোচনার কেন্দ্রে চলে আসে virtual DOM.
২০১৬ সালের নভেম্বরে Rich Harris “Frameworks without the framework” শিরোনামের লেখায় Svelte পরিচয় করান। প্রশ্ন ছিল: framework-এর বেশিরভাগ কাজ — কোন ডেটা কোন DOM-এ যায় — যদি build-এর সময়ই জানা যায়, তাহলে সেই হিসাবের কোড ব্রাউজারে পাঠানো কেন? Compiler component পড়ে সরাসরি text.data = count-এর মতো কোড বানাক। পরের বছর Ember-এর সহ-স্রষ্টা Tom Dale “Compilers are the New Frameworks” লেখায় এই প্রবণতাকে নাম দেন — বাক্যটি প্রায়ই ভুল করে Rich Harris-এর নামে উদ্ধৃত হয়। ২০১৯ সালে Svelte 3 আর Harris-এর “Rethinking Reactivity” বক্তৃতা ধারণাটিকে মূলধারায় আনে।
একই সময়ে Ryan Carniato SolidJS-এ (১.০, ২০২১) দেখান যে runtime-এ fine-grained signal দিয়েও virtual DOM ছাড়া খুব দ্রুত হওয়া যায়, আর JSX রেখেও। এরপর যেন বাঁধ ভাঙল: Preact Signals (২০২২), Angular 16-এ signal (২০২৩), Svelte 5-এর runes (২০২৪)। মজার মোড়: Svelte নিজেও শেষে runtime signal গ্রহণ করল।
২০২৪ সালের এপ্রিলে বিভিন্ন framework-এর লেখকদের নিয়ে তৈরি একটি দল TC39-এ Signals প্রস্তাব আনেন, যা Stage 1-এ পৌঁছায় — অর্থাৎ কমিটি সমস্যাটি নিয়ে কাজ করতে রাজি, কিন্তু ভাষায় আসবে কি না বা কী রূপে, তা নিশ্চিত নয়। লক্ষ্য সব framework-এর জন্য একটি সাধারণ ভিত্তি। React এই পথে যায়নি; তার উত্তর React Compiler.
মূল উপলব্ধি: প্রযুক্তির ইতিহাসে ভালো ধারণা প্রায়ই সময়ের আগে আসে, তারপর ভুলে যাওয়া হয়, তারপর নতুন নামে ফেরে। Signal আর compiler দুটিই একই প্রশ্নের উত্তর: “কী বদলেছে” হিসাব করার কাজ কখন আর কোথায় হবে — runtime-এ তুলনা করে, runtime-এ খাতা রেখে, নাকি build-এর সময়।
প্র ০৪২০১০-এর দশক ছিল SPA-র যুগ; ২০২০-এর দশকে Hotwire, htmx আর React Server Components আবার সার্ভারে HTML বানানোর দিকে ফিরছে। এই দোলক কেন দুলছে, আর এবার পার্থক্য কী?
ওয়েবের শুরু থেকে প্রায় দুই দশক HTML বানাত সার্ভার — PHP, Rails, Django, Java-র JSP. প্রতিটি ক্লিকে নতুন পাতা, পুরো পাতা আবার লোড। ২০১০-এর দশকে AngularJS, Backbone আর পরে React এনে দিল single-page application: একবার লোড, তারপর সব ব্রাউজারে, অ্যাপের মতো মসৃণ অনুভূতি। Gmail-এর মতো অ্যাপে এটি সঠিক ছিল, কিন্তু দ্রুতই ব্লগ আর রেস্টুরেন্টের মেনু পাতাও SPA হয়ে গেল।
দাম ধীরে ধীরে চোখে পড়ল। প্রথম লোডে ফাঁকা পাতা আর বড় JavaScript bundle; সস্তা ফোনে JavaScript parse করতেই কয়েক সেকেন্ড; SEO-র বাড়তি কৌশল; আর সার্ভার ও ব্রাউজারে দ্বিগুণ কাজ। বাংলাদেশের মতো বাজারে, যেখানে অনেক ব্যবহারকারী মাঝারি মানের ফোন আর মোবাইল ডেটায় ওয়েব দেখেন, এই খরচ সরাসরি ব্যবসা হারানো।
প্রতিক্রিয়া এল কয়েক দিক থেকে। Rails জগতে Basecamp-এর দল ২০২০ সালের ডিসেম্বরে Hotwire (Turbo) প্রকাশ করে: সার্ভার HTML টুকরো পাঠায়, একটি ছোট library সেগুলো পাতায় বসায় — SPA-র মসৃণতা, কিন্তু যুক্তি সার্ভারে। একই বছর Carson Gross তাঁর পুরোনো intercooler.js-এর উত্তরসূরি htmx আনেন, যা HTML attribute দিয়েই এ কাজ করে। React দল ২০২০ সালের ডিসেম্বরে Server Components ঘোষণা করে, যা পরে Next.js-এর App Router-এর ভিত্তি হয়। Astro-র islands আর ব্রাউজারের cross-document View Transitions MPA-কে আবার আকর্ষণীয় করেছে।
তবে এটি পুরোনো জায়গায় ফেরা নয়, সর্পিল পথে ওপরে ওঠা। আজকের সার্ভার রেন্ডারিং component দিয়ে লেখা, একই ভাষা দুই দিকে চলে, আর SPA-র ভালো দিকগুলো রেখে দেওয়ার চেষ্টা চলছে। প্রশ্ন আর “SPA নাকি MPA” নয়, বরং “এই পাতার কোন অংশ কোথায় চলা উচিত”.
মূল উপলব্ধি: প্রযুক্তির দোলক দোলে, কারণ প্রতিটি সমাধান নিজের সমস্যা তৈরি করে। দোলকের যেকোনো প্রান্তকে “একমাত্র সঠিক” ভাবা ভুল — কাজ ও ব্যবহারকারী অনুযায়ী সঠিক বিন্দু খোঁজাই প্রকৌশল।
প্র ০৫২০১৫–২০১৬ সালে “JavaScript fatigue” শব্দটি ছড়িয়ে পড়েছিল। framework আর টুলের এই দ্রুত বদলের মধ্যে একজন ডেভেলপার কীভাবে মাথা ঠান্ডা রাখবেন আর কোথায় সময় বিনিয়োগ করবেন?
২০১৫ সালের ডিসেম্বরে Eric Clemmons “Javascript Fatigue” শিরোনামে একটি লেখা প্রকাশ করেন, আর ২০১৬ সালের অক্টোবরে Jose Aguinaga-র ব্যঙ্গাত্মক “How it feels to learn JavaScript in 2016” ভাইরাল হয়। তাতে একজন শুধু একটি পাতায় ডেটা দেখাতে চান, আর তাঁকে শিখতে বলা হয় React, Babel, webpack, Redux, TypeScript… ব্যথাটি সত্য ছিল: কয়েক বছরের মধ্যে task runner Grunt থেকে Gulp, তারপর webpack; package manager Bower থেকে npm, তারপর Yarn; framework Backbone থেকে Angular, তারপর React — প্রতি বছর “আগেরটি পুরোনো হয়ে গেছে”.
দশ বছর পরে পেছনে তাকালে একটি আশ্চর্য সত্য চোখে পড়ে: অস্থিরতা ছিল মূলত উপরের স্তরে। React প্রায় এক দশক ধরে সবচেয়ে প্রচলিত UI library; component, one-way data flow, declarative UI — ধারণাগুলো বদলায়নি। Build tool বদলেছে (আজ Vite), কিন্তু module, bundling, transpiling-এর ধারণা একই। আর নিচের স্তর — JavaScript ভাষা, DOM, HTTP, event loop — প্রায় স্থির, শুধু বড় হয়েছে। এটি Lindy effect-এর উদাহরণ: যা অনেক দিন টিকে আছে, তা আরও অনেক দিন টেকার সম্ভাবনা বেশি।
তাই ব্যবহারিক কৌশল হলো স্তর অনুযায়ী বিনিয়োগ। সবচেয়ে বেশি সময় দিন সবচেয়ে স্থায়ী স্তরে — এই কোর্সের ভাষা, async, DOM, network. তারপর একটি প্রচলিত framework গভীরভাবে — শুধু API নয়, তার মডেল (render চক্র, reactivity). নতুন জিনিসে কৌতূহল রাখুন, কিন্তু প্রতিটি release-এ প্রজেক্ট বদলাবেন না। release note পড়ুন, ব্লগের “X is dead” শিরোনাম নয়।
AI-এর যুগে এই পরামর্শ আরও জোরালো: নতুন একটি framework-এর সিনট্যাক্স AI মুহূর্তে লিখে দেয়, কিন্তু কেন একটি component বারবার render হচ্ছে বা hydration কেন মিলছে না — তা বুঝতে লাগে ভিত্তির জ্ঞান।
মূল উপলব্ধি: ক্লান্তি আসে যখন প্রতিটি নতুন টুলকে আলাদা জ্ঞান মনে হয়। নিচের স্থায়ী ধারণাগুলো জানলে প্রতিটি নতুন framework একই গল্পের নতুন অধ্যায় মনে হয় — আর তখন বদল আর ভয়ের নয়, কৌতূহলের বিষয়।
অনুশীলন
প্রথম তিনটিতে কোড লিখতে হবে না — কাগজে উত্তর লিখে তারপর মিলিয়ে নিন।
-
state, props না derived? একটি বাস টিকিট বুকিং পাতায় আছে: (ক) বাছাই করা আসনগুলো, (খ) মোট ভাড়া, (গ) রুটের নাম (ওপরের পাতা থেকে আসে), (ঘ) “আসন ৪টির বেশি নেওয়া যাবে না” বার্তা দেখাবে কি না, (ঙ) ড্রপডাউন খোলা কি না, (চ) লগইন করা ব্যবহারকারীর নাম (পাতার অনেক জায়গায় লাগে)। প্রতিটিকে local state, shared state, props বা derived — কোনটি, বলুন।
(ক) local state — ব্যবহারকারীর দেওয়া, অন্য কিছু থেকে হিসাব করা যায় না (অন্য component-এর লাগলে ওপরে তুলুন)। (খ) derived — আসনের সংখ্যা × ভাড়া। (গ) props — parent দেয়, এই component বদলায় না। (ঘ) derived —
selected.length > 4; আলাদা boolean state রাখলে মেলানোর বাগ। (ঙ) local state — শুধু ড্রপডাউনের নিজের। (চ) shared state — অনেক component-এর লাগে, তাই ওপরে বা Context/store-এ। নিয়মটি: আগে জিজ্ঞেস করুন “হিসাব করা যায়?” — গেলে derived; না গেলে “কে কে লাগে?” — একজন হলে local, অনেকে হলে shared. -
key-এর বাগ অনুমান করুন: একটি todo তালিকায় [বাজার, বিল, ব্যায়াম], প্রতিটির পাশে একটি checkbox, আর
key={index}. ব্যবহারকারী “বিল”-এর পাশে টিক দিলেন (checkbox-এর অবস্থা DOM-এ, state-এ নয়), তারপর “বাজার” মুছে ফেললেন। এখন কোন আইটেমের পাশে টিক দেখা যাবে?key={todo.id}হলে?আগে: key 0 = বাজার, 1 = বিল (✔), 2 = ব্যায়াম। মোছার পরে: key 0 = বিল, 1 = ব্যায়াম। React দেখে key 0 আর 1 আছে, key 2 নেই — তাই প্রথম দুটি সারির লেখা বদলায় আর শেষ সারি (DOM-এর তৃতীয় সারি) সরায়। দ্বিতীয় সারির DOM রয়ে গেছে, তার checkbox-এ টিকও — এখন সেখানে লেখা “ব্যায়াম”. অর্থাৎ টিক চলে গেল “ব্যায়াম”-এ, আর “বিল” টিকহীন।
key={todo.id}হলে React “বাজার”-এর সারিটিই সরায়, বাকি দুটি অক্ষত — টিক “বিল”-এর পাশেই থাকে। এজন্যই checkbox-এর অবস্থাও state-এ (controlled) রাখা ভালো। -
সিদ্ধান্ত নিন: তিনটি প্রজেক্টের জন্য framework আর rendering-এর ধরন বেছে কারণ লিখুন — (১) একটি জাতীয় দৈনিকের অনলাইন সংস্করণ, (২) একটি গার্মেন্টস কারখানার অভ্যন্তরীণ উৎপাদন-ড্যাশবোর্ড (লগইন লাগে, live চার্ট), (৩) একটি বড় ব্যাংকের শাখা ব্যবস্থাপনার সফটওয়্যার, যা ৪০ জনের দল বহু বছর রক্ষণাবেক্ষণ করবে।
এক উত্তর নেই, যুক্তিই আসল। (১) দৈনিক: লেখা-প্রধান, SEO আর দ্রুত প্রথম লোড সবচেয়ে জরুরি, পাঠক মূলত মোবাইলে → SSR বা ISR (নতুন খবর মিনিটে মিনিটে) — Next.js বা Nuxt; বা Astro-র islands, যদি interactive অংশ কম। পুরো CSR SPA এখানে ভুল পছন্দ। (২) কারখানার ড্যাশবোর্ড: লগইনের পেছনে, SEO নেই, অনেক চার্ট → CSR যথেষ্ট; React বা Vue + Vite, live ডেটার জন্য পর্ব ৫১-এর WebSocket বা SSE. (৩) ব্যাংক: বড় দল, দীর্ঘমেয়াদ, কঠোর নিয়ম, টেস্টিং জরুরি → Angular-এর কাঠামো, TypeScript আর DI ভালো মিল; React বেছে নিলে দলকে নিজেই কঠোর নিয়ম (folder কাঠামো, state library, lint) ঠিক করে দিতে হবে।
-
কোড লিখুন — signal দিয়ে কার্ট: অংশ ৫-এর
signal/computed/effectব্যবহার করে একটি কার্ট বানান:cartএকটি signal (array),countআরtotalcomputed, আর একটি effect, যা একটি<p>-এ “কার্টে 2টি — ৳35” লেখে। তারপর ব্যাখ্যা করুন: কেনcart.get().push(item)লিখলে পাতা বদলায় না?একটি সম্ভাব্য সমাধান (অংশ ৫-এর তিনটি function আগে থেকে আছে ধরে):
const cart = signal([]); const count = computed(() => cart.get().length); const total = computed(() => cart.get().reduce((s, p) => s + p.price, 0)); const p = document.querySelector('#summary'); effect(() => { p.textContent = `কার্টে ${count.get()}টি — ৳${total.get()}`; }); const add = (item) => cart.set([...cart.get(), item]); // নতুন array add({ name: 'শিঙাড়া', price: 15 }); add({ name: 'চা', price: 20 }); // "কার্টে 2টি — ৳35"cart.get().push(item)একই array-কে ভেতর থেকে বদলায়, কিন্তুsetডাকা হয় না — তাই কেউ খবর পায় না। আরcart.set(cart.get())ডাকলেওv !== valueমিথ্যা (একই reference), তাই আবারও কিছু হয় না। এটি ঠিক React-এরcart.push(x); setCart(cart)বাগের মতো। Vue বা Svelte 5 এই সমস্যা এড়ায়, কারণ তারা array-কে Proxy দিয়ে মোড়ায় আরpush-ও টের পায় — পর্ব ২৮-এর Proxy-র কাজ।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমReact-এ একটি বোতাম চাপা থেকে পর্দা বদলানো পর্যন্ত ঘটনাগুলো সাজান (▲▼ চেপে বা টেনে)।
- 👆 event handler-এ setState ডাকা হয়
- 📝 React পরের render-এর সূচি করে (একই event-এর set-গুলো একসাথে)
- 🔁 component function আবার চলে, নতুন state পায়
- 🌳 নতুন virtual DOM গাছ তৈরি হয়
- 🔍 আগের গাছের সাথে তুলনা (reconciliation)
- ✅ শুধু পার্থক্যগুলো আসল DOM-এ বসে, তারপর effect চলে
React বা Vue-তে তালিকা আঁকার সময় প্রতিটি আইটেমে স্থায়ী key (যেমন ডেটাবেসের id) দেওয়ার মূল কারণ কী?
- key ছাড়া HTML অবৈধ হয়ে যায়
- framework key অনুযায়ী তালিকা সাজিয়ে দেখায়
- আগের আর নতুন render-এ কোন আইটেম কোনটি, তা চিনে সঠিক সারি রাখা, সরানো বা বানানো
- key ব্যবহারকারীর ডেটাকে XSS থেকে রক্ষা করে
React-এর JSX <p className="x">{total}</p> build-এর পরে মূলত কীতে পরিণত হয়?
- একটি HTML string, যা innerHTML-এ বসানো হয়
- একটি function call, যা element-এর বর্ণনা দেওয়া একটি object ফেরত দেয়
- সরাসরি একটি আসল DOM element
- একটি template, যা ব্রাউজারের ভেতরের template engine পড়ে
React-এর rules of hooks অনুযায়ী নিচের কোনটি ভুল?
- component-এর একেবারে শুরুতে দুটি useState ডাকা
- useCart নামের নিজের hook-এর ভেতরে useState ডাকা
- useEffect-এর ভেতরে if দিয়ে শর্ত দেখা
if (loggedIn) { const [x, setX] = useState(0); }
Server-side rendering-এর পরে ব্রাউজারে “hydration” ধাপে কী ঘটে?
- সার্ভার থেকে আসা বিদ্যমান HTML-এ event listener আর state লাগিয়ে পাতাকে interactive করা
- সার্ভারের পাঠানো HTML প্রথমবার পর্দায় আঁকা
- সার্ভারে component চালিয়ে HTML বানানো
- পাতার সব ছবি আর CSS নামানো শেষ করা
signal-ভিত্তিক fine-grained reactivity-তে (যেমন Solid বা Angular signal) একটি signal-এর মান বদলালে কী আবার চলে?
- পুরো component function, তারপর virtual DOM তুলনা
- পাতার সব effect
- শুধু যেসব computed আর effect ওই signal পড়েছিল
- পুরো পাতা reload হয়
একটি সফটওয়্যার কোম্পানির ডকুমেন্টেশন সাইট — শত শত লেখার পাতা, প্রায় কোনো interactivity নেই, SEO খুব জরুরি। কোন পছন্দ সবচেয়ে যুক্তিসংগত?
- Client-side rendered React SPA
- SSG — যেমন Astro, দরকার হলে কয়েকটি interactive island
- প্রতিটি অনুরোধে SSR, সাথে পুরো পাতার hydration
- Angular দিয়ে পূর্ণ এন্টারপ্রাইজ অ্যাপ
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখতে পাবেন।
মনে রাখুন
- Framework-এর কাজ state আর DOM মিলিয়ে রাখা। আপনি বর্ণনা করেন “এই state-এ পাতা এমন” (UI = f(state)), framework DOM-এ ন্যূনতম পরিবর্তন করে; পাতা ভাগ হয় component-এ।
- ভাগ করা ভাষা: props নিচে, event ওপরে (one-way data flow); state কম, derived বেশি; effect শুধু বাইরের জগতের জন্য, cleanup-সহ; তালিকায় স্থায়ী key.
- চার পথ: React — পুরো component আবার চালানো + virtual DOM diff; Vue — Proxy দিয়ে নির্ভরতা নোট; Svelte — compiler আর runes; Solid/Angular/Preact — signal দিয়ে শুধু নির্ভরশীল অংশ।
- Rendering: CSR (ব্রাউজারে), SSR (প্রতি অনুরোধে সার্ভারে), SSG (build-এ), ISR (মাঝে মাঝে নতুন); hydration সার্ভারের HTML-কে জীবন্ত করে; islands, RSC আর resumability পাঠানো JavaScript কমায়।
- বাছাই প্রজেক্ট আর দল অনুযায়ী — আর ছোট সাইটে vanilla JS, htmx বা Alpine যথেষ্ট। আগে JavaScript-এর ভিত্তি, তারপর একটি framework গভীরভাবে; AI-এর কোডে সংস্করণ, effect, key আর global state যাচাই করুন।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৫৫ · ক্যাপস্টোন — একটি full-stack অ্যাপ বানানো ও deployপরবর্তী পর্বএই কোর্সের সবকিছু একটি সত্যিকারের প্রজেক্টে।
- JavaScript প্রোগ্রামিং · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- পর্ব ৩৭ · DOM — tree, খোঁজা আর বদলানোফিরে দেখুনframework যে API-গুলো ভেতরে ডাকে, আর virtual DOM-এর প্রথম ঝলক।
- Full-Stack Web Frameworks কোর্সগভীরে যানcomponent, state management, SSR, SSG আর hydration — স্থাপত্যের চোখে।
- Mobile App Development কোর্সতুলনা করুনReact Native, Flutter আর declarative UI — একই ধারণা মোবাইলে।