সরাসরি পর্বে যান
পর্ব ০৭ · ৯৩-এর মধ্যে · মডিউল ২

React কেন — component-এ UI ভাবা

Why React — Thinking of the UI in Components

Taught in Bangla, this lesson explains the problem React solves, keeping the screen in step with data, splitting a page into a component tree, function components with names, return values and Fragments, a first component in a real Vite project, the path from createRoot to the DOM, purity in rendering, why StrictMode runs components twice in development with a real run, React DevTools, file habits, and what React does not do on its own.

এই পর্বে যা শিখবেন

  • React কোন সমস্যা মেটায়, আর declarative ভাবনা বাস্তবে কী বদলায়
  • “বাজারঘর”-এর পণ্যের পাতাকে component-এর গাছে ভাগ করা
  • Function component লেখা, নাম দেওয়া, একটি root আর Fragment
  • Vite project-এ main.tsx, createRoot আর একটি render-এর পুরো পথ
  • Purity ভাঙলে কী হয়, আর StrictMode সেটি কীভাবে ধরে
  • React DevTools দিয়ে গাছ দেখা, আর ফাইল সাজানোর অভ্যাস

১ · React কোন সমস্যা মেটায় — পর্দা আর data মিলিয়ে রাখা

ক্রিকেট মাঠের পুরোনো স্কোরবোর্ড ভাবুন। একজন মানুষ হাতে নম্বরের পাত বদলান। একটি চার হলে তাঁকে রান বদলাতে হয়, ব্যাটারের রান বদলাতে হয়, বলের সংখ্যাও বদলাতে হয়। একটি ভুলে গেলেই বোর্ড মিথ্যা বলে। অথচ স্কোরারের খাতায় হিসাব ঠিকই আছে।

ডিজিটাল স্কোরবোর্ড অন্যভাবে চলে। সেখানে খাতাটিই আসল। বোর্ড শুধু খাতা দেখে নিজেকে সাজায়। কে কী বদলাবে, তা আলাদা করে কাউকে বলতে হয় না।

“বাজারঘর”-এর পণ্যের পাতাতেও একই সমস্যা। ক্রেতা “কার্টে যোগ করুন” চাপলে অন্তত চারটি জায়গা বদলানোর কথা:

  • হেডারের কার্ট আইকনের সংখ্যা
  • কার্টের মোট টাকা
  • পণ্যের স্টকের লেখা (“মাত্র ২টি বাকি”)
  • বাটনের অবস্থা (স্টক শেষ হলে বন্ধ)

হাতে DOM বদলালে এই চারটি জায়গার হিসাব আপনাকেই রাখতে হয়। পরে কেউ পঞ্চম একটি জায়গা যোগ করলে আগের সব কোড খুঁজে বদলাতে হয়। একটি জায়গা বাদ পড়লে কোনো error আসে না, শুধু পর্দা ভুল কথা বলে। এই ধরনের বাগ ধরা কঠিন, কারণ প্রোগ্রাম ভাঙে না।

Imperative (হাতে DOM)Declarative (React)
আপনি যা লেখেন“এই ঘরটি খুঁজে লেখা বদলান”“qty এত হলে পর্দা এমন দেখাবে”
সত্যের জায়গাDOM আর ভ্যারিয়েবল, দুই জায়গায়শুধু data-য়
নতুন জায়গা যোগ হলেপ্রতিটি ঘটনার কোড বদলাতে হয়একবার বর্ণনা করলেই হয়
ঝুঁকিবদলানো ভুলে যাওয়াঅকারণ হিসাব, যা পরে শিখবেন

এটুকু আপনি পর্ব ১-এ দেখেছেন: রিকশাওয়ালাকে প্রতিটি মোড়ে পথ বলা, আর শুধু গন্তব্য বলা। সেখানে ছোট সূত্রটি ছিল UI = f(state). এর মানে, পর্দা হলো state-এর একটি ফাংশন। ভিত্তির আলোচনা আছে JavaScript course-এর framework পর্বে. এই মডিউলে আমরা সূত্রটি হাতে-কলমে বানাব।

React তৈরি হয় Facebook-এ। সাধারণভাবে এর স্রষ্টা হিসেবে Jordan Walke-র নাম বলা হয়। ২০১৩ সালের মে মাসে JSConf US সম্মেলনে এটি open source হয়। শুরুতে অনেকেই JSX দেখে আপত্তি করেছিলেন, কারণ তখনকার নিয়ম ছিল HTML আর JavaScript আলাদা রাখা। ২০১৯ সালের ফেব্রুয়ারিতে React 16.8-এ hook আসে। এরপর function component-ই প্রধান পথ হয়ে যায়। লেখার সময়ে npm-এ React-এর stable version 19.3.1.

২ · Component-এ ভাবা — একটি পাতাকে গাছে ভাগ করা

দর্জি একটি পাঞ্জাবিকে এক টুকরো হিসেবে কাটেন না। তিনি হাতা, কলার, বুক আলাদা মাপে কাটেন, পরে জোড়া দেন। হাতার মাপ বদলালে কলার ছুঁতে হয় না। Component ঠিক এই কাটা টুকরো। প্রতিটি টুকরোর একটি কাজ, আর জোড়া দিলে পুরো পাতা।

প্রশ্ন হলো, কোথায় কাটবেন? নিচের তিনটি প্রশ্ন বেশিরভাগ ক্ষেত্রে কাজ দেয়। পণ্যের পাতায় ধাপে ধাপে লাগিয়ে দেখুন।

  1. এক কথায় বলা যায়? Component-এর কাজ বলতে “এবং” লাগলে সেটি সম্ভবত দুটি component.
  2. এটি কি বারবার আসে? একই চেহারা দুই জায়গায় থাকলে একটি component বানালে দুই জায়গা একসঙ্গে ঠিক থাকে।
  3. এটি কি আলাদা কারণে বদলায়? দাম বদলায় ব্যবসার নিয়মে, লোগো বদলায় নকশার কারণে। কারণ আলাদা হলে জায়গাও আলাদা হওয়া ভালো।
ProductPage.tsxসব এক ফাংশনে
ProductPage
  লোগো, খোঁজার ঘর, কার্ট
  পণ্যের ছবি, নাম, দাম, স্টক
  “কার্টে যোগ করুন”
  মিল আছে এমন ৪টি পণ্য
  ঠিকানা আর ফোন নম্বর
প্রথম কাটপাতার বড় এলাকা
ProductPage
  Header
  ProductDetails
  RelatedProducts
  Footer
Headerআলাদা কাজ, আলাদা বদল
ProductPage
  Header
    Logo
    SearchBox
    CartButton
  ProductDetails
  RelatedProducts
  Footer
ProductDetailsদাম আর স্টকের নিজস্ব নিয়ম
ProductPage
  Header …
  ProductDetails
    ProductGallery
    ProductInfo
      PriceTag
      StockBadge
    AddToCart
  RelatedProducts
  Footer
RelatedProductsএকই টুকরো আবার
ProductPage
  Header …
  ProductDetails …
  RelatedProducts
    ProductCard × 4
      PriceTag    ← আবার ব্যবহার
      StockBadge  ← আবার ব্যবহার
  Footer
  1. ধাপ ১ — এক ফাংশনে সব। এমনটি চলে, কিন্তু খোঁজার ঘর বদলাতে গেলে পুরো পাতার কোড পড়তে হয়। এটিই god component-এর শুরু। প্রশ্ন ১ দিয়ে দেখুন: এর কাজ বলতে অনেকগুলো “এবং” লাগে।
  2. ধাপ ২ — বড় এলাকা। চোখে দেখেই পাতা চার ভাগ হয়। এখানে HTML-এর header, main, footer ভাবনার সঙ্গে মিল আছে। এই কাঠামো নিয়ে আছে semantic layout পর্ব।
  3. ধাপ ৩ — Header ভাঙা। লোগো প্রায় বদলায় না। খোঁজার ঘরে টাইপ করার নিজস্ব অবস্থা থাকবে। কার্টের সংখ্যা কার্টের data-র ওপর চলে। বদলের কারণ তিনটি, তাই component তিনটি।
  4. ধাপ ৪ — পণ্যের অংশ। দামের লেখা “৳১২০”, ছাড়ের হিসাব, স্টকের লেখা: এগুলোর নিয়ম আছে। PriceTag আর StockBadge আলাদা হলে নিয়ম এক জায়গায় থাকে।
  5. ধাপ ৫ — আবার ব্যবহার। প্রশ্ন ২-এর উত্তর এখানে মেলে। একই ProductCard চারবার বসে, আর তার ভেতরে আগের PriceTag. দামের চেহারা একবার বদলালে সব জায়গায় বদলাবে। পণ্যের data-র গঠনও প্রায় এই গাছের মতো, যা ভালো ভাগের একটি লক্ষণ।

“বাজারঘর”-এর পণ্যের পাতার component গাছ

Component tree — প্রতিটি বাক্স একটি ফাংশন, তারকা চিহ্ন মানে আবার ব্যবহার

বাজারঘর-এর পণ্যের পাতার component গাছ ProductPage Header Logo SearchBox CartButton ProductDetails ProductGallery ProductInfo ★ PriceTag ★ StockBadge AddToCart RelatedProducts ProductCard × 4 ★ PriceTag ★ StockBadge Footer পাতার মাথা মূল পণ্য দামের নিয়ম এক জায়গায় মিল আছে চারবার বসে পাতার নিচ
ছবি ৭.১ — গাছে ১৪ রকম component, কিন্তু পাতায় মোট ২৫টি বসেছে। আবার ব্যবহারের সুবিধা এখানেই। হিসাবটি অনুশীলনে করবেন।

উল্টো ভুলও আছে। তিন লাইনের প্রতিটি div-কে component বানালে কোড পড়তে দশটি ফাইল খুলতে হয়। সীমাটি নির্দিষ্ট নয়, দল ভেদে মত আলাদা। নতুনদের জন্য একটি সহজ নিয়ম: আগে কয়েকটি বড় component লিখুন, ব্যথা শুরু হলে ভাঙুন।

৩ · Function component — নাম, return আর Fragment

React-এর component একটি সাধারণ JavaScript ফাংশন, যা পর্দার একটি বর্ণনা ফেরত দেয়। JavaScript-এর ফাংশন আপনি চেনেন। বাড়তি কথা শুধু তিনটি। নিচের ঘরে কোড বদলে নিজেই চালান। এটি পাতার ভেতরে চলা আসল React 19, তবে এর production build.

<div id="root"></div>
<script src="../course-kit/vendor/react-lab.js"></script>
<script type="text/jsx">
function PriceTag() {
  console.log('PriceTag');
  return <p style={{ fontWeight: 700 }}>৳১২০</p>;
}
function StockBadge() {
  console.log('StockBadge');
  return <span style={{ color: '#067' }}>স্টকে আছে</span>;
}
function ProductCard() {
  console.log('ProductCard');
  return (
    <article style={{ border: '1px solid #ccc', padding: 12 }}>
      <h2>মিনিকেট চাল, ১ কেজি</h2>
      <PriceTag />
      <StockBadge />
    </article>
  );
}
createRoot(document.getElementById('root')).render(<ProductCard />);
</script>

পাতায় একটি কার্ড দেখাবে, আর নিচে আসবে ProductCard, PriceTag, StockBadge, এই ক্রমে। React আগে বাইরের component ডাকে, তার ফল দেখে ভেতরের component ডাকে। লক্ষ করুন, আপনি নিজে কোথাও PriceTag() ডাকেননি। আপনি শুধু <PriceTag /> লিখেছেন, ডাকার দায়িত্ব React-এর। style-এর জোড়া বন্ধনী আর JSX-এর বাকি নিয়ম আসবে পর্ব ৮-এ।

তিনটি নিয়ম, আর ভাঙলে আসল বার্তা

নাম বড় হাতের অক্ষরে শুরু হবে। এই লেখার ধরনকে বলে PascalCase: ProductCard, PriceTag. এটি শুধু সৌন্দর্যের নিয়ম নয়। JSX-এ ছোট হাতের ট্যাগ মানে HTML ট্যাগ, আর বড় হাতের ট্যাগ মানে আপনার component.

একটি root ফেরত দিতে হবে। JavaScript-এর একটি ফাংশন একটি মানই ফেরত দিতে পারে। পাশাপাশি দুটি ট্যাগ মানে দুটি মান। তাই সেগুলোকে একটি মোড়কে রাখতে হয়।

কিছু একটা ফেরত দিতে হবে। return ভুলে গেলে ফাংশন undefined দেয়, পর্দায় কিছুই আসে না।

ভুলকে ধরেআসল বার্তা
পাশাপাশি h1 আর ptscTS2657: JSX expressions must have one parent element.
একই ভুলvite buildAdjacent JSX elements must be wrapped in an enclosing tag. সঙ্গে: Help: Did you want a JSX fragment `<>...</>`?
<productCard />tscTS2339: Property 'productCard' does not exist on type 'JSX.IntrinsicElements'.
একই ভুল, dev-এ চালালেBrowser consoleThe tag <productCard> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
return ভুলে যাওয়াtscTS2786: 'PriceTag' cannot be used as a JSX component.

সত্যিকারের run · React 19.3.1, Vite 8.3.5, TypeScript 7.0.2 · লম্বা বার্তা ছাঁটা

চতুর্থ সারির ঘটনাটি মনে রাখুন। ছোট হাতের ভুলে npm run dev থামেনি। পাতা খালি এলো, আর DOM-এ বসল <productcard></productcard> নামের একটি অচেনা ট্যাগ। কারণ dev server শুধু অনুবাদ করে, type যাচাই করে না। যাচাই হয় tsc -b-তে। পর্ব ৫-এর npm run build-এ এটি প্রথমেই চলে।

Fragment — ছাপ না রাখা মোড়ক

div দিয়ে মুড়ে দিলে কাজ চলে, কিন্তু DOM-এ একটি অকারণ স্তর জমে। CSS Grid বা Flexbox-এর সরাসরি সন্তান বদলে গেলে নকশাও ভাঙতে পারে। আর ul-এর সরাসরি সন্তান li না হলে পর্দা পাঠকের কাছে তালিকার অর্থ দুর্বল হয়; দেখুন accessibility পর্ব।

সমাধান <>…</>. এই খালি ট্যাগের নাম Fragment. ডাকঘরের বস্তার মতো ভাবুন: পরিবহনের সময় চিঠিগুলো এক সঙ্গে থাকে, কিন্তু বিলির সময় বস্তাটি কারও বাড়ি যায় না। আমাদের আসল ProductPage ঠিক এটি ব্যবহার করছে।

import { Header } from '../components/Header'
import { ProductCard } from '../components/ProductCard'

export function ProductPage() {
  return (
    <>
      <Header />
      <main>
        <ProductCard />
      </main>
    </>
  )
}

৪ · আসল project — Vite-এ প্রথম component

এবার পাতার ঘর থেকে নিজের কম্পিউটারে আসি। পর্ব ৫-এর কমান্ডই চলবে, তাই ব্যাখ্যা আবার দিচ্ছি না।

npm create vite@latest bazarghar-web -- --template react-ts --no-interactive
cd bazarghar-web
npm install
npm run dev

টেমপ্লেটের App.css আর assets মুছে আমরা নিজেদের ফাইল বসিয়েছি। ফাইলের গঠন এখন এমন:

src/
  main.tsx
  App.tsx
  index.css
  pages/
    ProductPage.tsx
  components/
    Header.tsx
    ProductCard.tsx
    PriceTag.tsx
    StockBadge.tsx
import { PriceTag } from './PriceTag'
import { StockBadge } from './StockBadge'

export function ProductCard() {
  return (
    <article className="card">
      <h2>মিনিকেট চাল, ১ কেজি</h2>
      <PriceTag />
      <StockBadge />
    </article>
  )
}
export function PriceTag() {
  return <p className="price">৳১২০</p>
}
import { ProductPage } from './pages/ProductPage'

export default function App() {
  return <ProductPage />
}

className কেন, class কেন নয়, সেটি পর্ব ৮-এ। দাম এখানে হাতে লেখা, কারণ বাইরে থেকে মান পাঠানো (props) পর্ব ৯-এর বিষয়। এবার build:

$ npm run build

> bazarghar-web@0.0.0 build
> tsc -b && vite build

vite v8.3.5 building client environment for production...
transforming...
✓ 21 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html                   0.46 kB │ gzip:  0.29 kB
dist/assets/index-DJjl32jt.css    0.16 kB │ gzip:  0.15 kB
dist/assets/index-CyGVGsQH.js   220.14 kB │ gzip: 68.86 kB

✓ built in 96ms

সত্যিকারের run · Node 24.15.0, Vite 8.3.5, React 19.3.1 · একটি স্থানীয় run-এর সংখ্যা, সাধারণ ফল নয়

হিসাব

পাঁচটি component লিখেও JavaScript হলো 220.14 kB, প্রায় পুরোটাই React আর react-dom. পর্ব ৫-এর টেমপ্লেটে ছিল 222.44 kB. মানে শুরুতে আপনার component নয়, library-র ওজনই বেশি।

ধরে নিন গতি 1 Mbps (অনুমান)। 68.86 × 8 = 550.88 kilobit, সময় 550.88 ÷ 1000 ≈ 0.55 সেকেন্ড. এই খরচ দেওয়ার পরই React কাজ শুরু করতে পারে। এজন্যই সাধারণ লেখার পাতায় React লাগে কি না ভাবা দরকার। পর্ব ৩-এ সেই সিদ্ধান্তের মানচিত্র আছে।

main.tsx — যেখানে React জীবনে আসে

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

দুটি আলাদা package লক্ষ করুন। react-এ থাকে component আর hook-এর মূল ধারণা। react-dom/client জানে browser-এর DOM কীভাবে বদলাতে হয়। এই ভাগের জন্যই একই React ধারণা ফোনের অ্যাপেও চলে, শুধু নিচের অংশ আলাদা। আর ! চিহ্নটি TypeScript-কে বলে “এটি null হবে না”। এটি পর্ব ৬-এর সেই চলার সময়ে যাচাই নেই ধরনের প্রতিশ্রুতি। প্রতিশ্রুতি ভাঙলে কী হয়, আমরা id ভুল লিখে দেখেছি:

Target container is not a DOM element.

সত্যিকারের run · getElementById('roott') লিখে Edge-এ dev server চালিয়ে · tsc এই ভুল ধরে না, পাতা খালি থাকে

৫ · createRoot থেকে DOM — একটি render-এর যাত্রা

রেস্তোরাঁর কথা ভাবুন। ওয়েটার অর্ডার লিখে রান্নাঘরে দেন (render-এর অনুরোধ)। রান্নাঘরে খাবার তৈরি হয় (render), তখনও টেবিলে কিছু নেই। সব তৈরি হলে একবারে টেবিলে আসে (commit). এই দুটি ধাপ আলাদা, আর এটিই React বোঝার চাবি।

আমাদের project-এ component আছে ছয়টি: App, ProductPage, Header, ProductCard, PriceTag, StockBadge. প্রথম render ধাপে ধাপে দেখুন।

1<div id="root"></div>2const root = createRoot(el);3root.render(<App />);4App() → ProductPage() → …5DOM-এ ট্যাগ বসানো6useEffect থাকলে চলে
এই মুহূর্তের অবস্থা
ধাপের নাম
ফাংশন ডাকা হয়েছে
#root-এর ভেতরে
পর্দায়
  1. ধাপ ১ — খালি ঘর। Browser index.html পড়ে। ভেতরে একটি খালি div#root আর main.tsx-এর script. এটিই পর্ব ৩-এর CSR: HTML প্রায় ফাঁকা।
  2. ধাপ ২ — ঘরের দখল। createRoot(el) একটি React root তৈরি করে। এখন থেকে এই ঘরের ভেতরের DOM React-এর দায়িত্বে। কোনো component এখনো চলেনি।
  3. ধাপ ৩ — অনুরোধ। root.render(<App />) React-কে জানায় কী দেখাতে হবে। <App /> নিজে একটি সাধারণ বর্ণনা object, ফাংশন-ডাক নয়। React কাজটি সূচিতে রাখে।
  4. ধাপ ৪ — render. React App ডাকে, ফল দেখে ProductPage ডাকে, এভাবে ছয়টি ফাংশন। DOM-এ এখনো হাত পড়েনি, শুধু হিসাব চলছে।
  5. ধাপ ৫ — dev-এর বাড়তি ডাক। StrictMode-এ প্রতিটি component-কে আরও একবার ডাকা হয়, তাই ৬ × ২ = ১২। আসলে দুটি ডাক পাশাপাশি হয়, সব শেষে নয়। Production-এ এই ধাপ নেই।
  6. ধাপ ৬ — commit. হিসাব শেষে React আসল ট্যাগ তৈরি করে #root-এ বসায়। root.render প্রথমবার ডাকলে ঘরের আগের HTML মুছে যায়। এবার browser পর্দায় আঁকে।
  7. ধাপ ৭ — effect. Commit-এর পরে useEffect-এর কাজ চলে। আমাদের এই project-এ effect নেই, তাই কিছু ঘটে না। Effect নিয়ে পুরো আলোচনা পর্ব ১৩-এ। State বদলালে ধাপ ৪ থেকে আবার শুরু হয়, সেটি পর্ব ১০-এ।

“বর্ণনা” কথাটি একটু অদ্ভুত লাগতে পারে। তাই এবার একটি খেলনা React বানিয়ে দেখি। এটি আসল React-এর কোড নয়। এটি শুধু দেখায়, ফাংশন থেকে বর্ণনা আর বর্ণনা থেকে HTML কীভাবে আসে।

// Toy only: real React does far more. This shows the core idea.
const h = (type, props, ...kids) => ({ type, props: props ?? {}, kids });

function PriceTag({ taka }) {
  return h('b', null, 'Tk ' + taka);
}
function Card({ name, taka }) {
  return h('article', null, h('h2', null, name), h(PriceTag, { taka }));
}
function App() {
  return h('main', null, h(Card, { name: 'Rice', taka: 120 }), h(Card, { name: 'Dal', taka: 140 }));
}

// "Render": call each function, turn the tree into text
function expand(node, depth = 0) {
  if (typeof node === 'string') return node;
  if (typeof node.type === 'function') {
    console.log(' '.repeat(depth * 2) + 'call ' + node.type.name);
    return expand(node.type(node.props), depth + 1);
  }
  const inner = node.kids.map((kid) => expand(kid, depth + 1)).join('');
  return `<${node.type}>${inner}</${node.type}>`;
}

const element = h(App, null);
console.log('element:', JSON.stringify(h('b', null, 'Tk 120')));
console.log('type is a function?', typeof element.type === 'function');
const html = expand(element);
console.log('commit:', html);

প্রথম লাইন element: {"type":"b","props":{},"kids":["Tk 120"]}: একটি “ট্যাগ” আসলে সাধারণ object. তারপর call App, call Card, call PriceTag, আবার call Card, call PriceTag: বাইরে থেকে ভেতরে, গাছ ধরে। শেষ লাইনে পুরো HTML. আসল React-এর সঙ্গে বড় পার্থক্য হলো, সে লেখা না বানিয়ে DOM বদলায়, আর পরের render-এ আগের গাছের সঙ্গে মিলিয়ে দেখে কতটুকু বদলাতে হবে। সেই তুলনা পর্ব ১৫-এ।

JSX যে এই ধরনের ফাংশন-ডাকে বদলায়, তার প্রমাণ dev server-ই দেয়। ProductCard.tsx চাইলে browser JSX পায় না, পায় এটি:

export function ProductCard() {
    return /* @__PURE__ */ _jsxDEV("article", {
        className: "card",
        children: [
            /* @__PURE__ */ _jsxDEV("h2", { children: "মিনিকেট চাল, ১ কেজি" }, …),
            /* @__PURE__ */ _jsxDEV(PriceTag, {}, …),
            /* @__PURE__ */ _jsxDEV(StockBadge, {}, …)
        ]
    }, …);
}

সত্যিকারের run · curl দিয়ে Vite 8.3.5 dev server থেকে আনা, লম্বা অংশ ছাঁটা

দেখুন, "article" একটি লেখা, কিন্তু PriceTag একটি ফাংশনের নাম। ছোট-বড় হাতের নিয়মের কারণ এখানেই।

৬ · Purity — একই ইনপুটে একই ফল

দর্জিকে একই মাপ দিলে একই পাঞ্জাবি আসে। মাপ দেখার সময় তিনি আপনার পকেটের টাকা গোনেন না, পাশের দোকানের খাতায় কিছু লেখেন না। React component-এর কাছেও একই চাওয়া। Pure ফাংশন মানে দুটি কথা:

  • নিজের কাজে থাকে: ডাকার আগে থেকে থাকা কোনো ভ্যারিয়েবল বা object বদলায় না।
  • একই ইনপুটে একই ফল: একই props, state আর context পেলে একই বর্ণনা ফেরত দেয়।

React কেন এতে জোর দেয়? কারণ render শুধু হিসাব। React চাইলে সেটি বেশি বার করতে পারে, মাঝপথে ফেলেও দিতে পারে। ফাংশন বাইরের কিছু বদলালে প্রতিটি বাড়তি ডাকে হিসাব নষ্ট হয়। এবার ভাঙা উদাহরণটি দেখুন। “বাজারঘর”-এর টিকিট নম্বর একটি বাইরের ভ্যারিয়েবল থেকে আসছে। React.dev-এর guest counter উদাহরণের এটি একটি দেশি রূপ।

1let guest = 0;2function OrderTicket() {3  guest = guest + 1;4  return <p>অর্ডার #{guest}</p>;5}6// সমাধান7function OrderTicket({ n }) {8  return <p>অর্ডার #{n}</p>;9}
তিনটি OrderTicket, StrictMode চালু
guest
কোন টিকিট
কোন ডাক
এই ডাকের ফল
  1. শুরু। ফাংশনের বাইরে guest = 0. সমস্যাটি এখানেই বসে আছে: মানটি component-এর ইনপুট নয়, তবু ফল এর ওপর নির্ভর করছে।
  2. ১ম টিকিট, ১ম ডাক। guest হলো 1, ফল #1. Dev-এ StrictMode এখনই আরেকবার ডাকবে।
  3. ১ম টিকিট, ২য় ডাক। Pure হলে ফল আবার #1 হতো। কিন্তু guest বেড়ে 2, ফল #2. পর্দায় যাবে এই দ্বিতীয় ফলটি।
  4. ২য় টিকিট, ১ম ডাক। guest আগের জায়গা থেকেই চলছে: 3, ফল #3.
  5. ২য় টিকিট, ২য় ডাক। guest 4, ফল #4. এটিই পর্দায় যাবে।
  6. ৩য় টিকিট, ১ম ডাক। guest 5, ফল #5.
  7. ৩য় টিকিট, ২য় ডাক। guest 6, ফল #6. তিনটি টিকিটের জন্য ছয়টি ডাক শেষ।
  8. Commit. পর্দায় এলো #2, #4, #6. Vite-এর আসল dev run-এ ঠিক এই ফল এসেছে। একই কোড production-এ #1, #2, #3 দেখায়, তাই ভুলটি সেখানে লুকিয়ে থাকে। কিন্তু সেখানেও কোনো একটি re-render হলেই নম্বর সরে যাবে।
  9. সমাধান। নম্বরটি ইনপুট বানান: <OrderTicket n={1} />. বাইরের guest আর নেই। যতবার ডাকুন, ফল একই, তাই পর্দায় #1, #2, #3.

নিচের ঘরে একই ভুল চালিয়ে দেখুন। এই ঘরের React production build, তাই StrictMode এখানে দুবার ডাকে না। আমরা কাজটি হাতে করছি: একই পাতা দুবার render করছি, যেমনটি যেকোনো state বদলে হয়।

<div id="root"></div>
<script src="../course-kit/vendor/react-lab.js"></script>
<script type="text/jsx">
let guest = 0;                     // component-এর বাইরে
function BadTicket() {
  guest = guest + 1;               // ভুল: render-এ বাইরের মান বদলানো
  return <p>bad #{guest}</p>;
}
function GoodTicket({ n }) {       // সব তথ্য ইনপুট থেকে
  return <p>good #{n}</p>;
}
function Page() {
  return (
    <>
      <BadTicket />
      <BadTicket />
      <GoodTicket n={1} />
      <GoodTicket n={2} />
    </>
  );
}
const el = document.getElementById('root');
const root = createRoot(el);
for (let pass = 1; pass <= 2; pass++) {
  ReactDOM.flushSync(() => root.render(<Page />));
  const seen = [...el.children].map((p) => p.textContent);
  console.log('render ' + pass + ':', seen.join(', '));
}
</script>

আউটপুট দুই লাইন: render 1: bad #1, bad #2, good #1, good #2 আর render 2: bad #3, bad #4, good #1, good #2. ইনপুট কিছুই বদলায়নি, তবু BadTicket-এর লেখা বদলে গেছে। GoodTicket দুবারই এক। ReactDOM.flushSync এখানে শুধু দেখানোর সুবিধার জন্য: এটি render শেষ হওয়া পর্যন্ত অপেক্ষা করায়। সাধারণ অ্যাপে এর দরকার প্রায় পড়ে না।

তাহলে কী চলবে, কী চলবে না

কাজRender-এর ভেতরে?কারণ বা বিকল্প
নিজের ভেতরে নতুন array বানিয়ে pushচলবেএই render-এই জন্ম, বাইরে কেউ দেখে না (local mutation)
Props থেকে দাম হিসাবচলবেএকই ইনপুটে একই ফল
বাইরের ভ্যারিয়েবল বদলানোচলবে নাProps বা state ব্যবহার করুন
পাওয়া props-এর array-তে pushচলবে নাকপি করে বদলান; props অন্যের জিনিস
Date.now(), Math.random()চলবে নাপ্রতি ডাকে ফল আলাদা হয়
fetch, localStorage লেখা, DOM বদলানোচলবে নাএগুলো side effect; রাখুন event handler বা effect-এ

Side effect নিষিদ্ধ নয়, শুধু তার জায়গা আলাদা। ক্লিকের পরের কাজ যায় event handler-এ (পর্ব ১০), কারণ সেটি render নয়। আর কোনো event ছাড়াই বাইরের সিস্টেমের সঙ্গে মিলতে হলে আছে useEffect (পর্ব ১৩). তবে react.dev একে শেষ উপায় হিসেবেই দেখতে বলে।

৭ · StrictMode — dev-এ কেন দুবার চলে

গার্মেন্টসের রপ্তানির আগে পরীক্ষক একই শার্ট দুবার টেনে দেখেন। ভালো সেলাই টেকে, দুর্বল সেলাই খুলে যায়। শার্ট ক্রেতার কাছে যায় পরীক্ষা ছাড়াই, কারণ পরীক্ষাটি কারখানার ভেতরের। StrictMode সেই পরীক্ষক। এটি পর্দায় কিছু দেখায় না, শুধু dev-এ ভুল বের করে আনে। আমরা একই ভাঙা কোড তিনভাবে চালিয়েছি, সঙ্গে একটি useEffect রেখে:

render App
render App
render OrderTicket, guest = 1
render OrderTicket, guest = 2
render OrderTicket, guest = 3
render OrderTicket, guest = 4
render OrderTicket, guest = 5
render OrderTicket, guest = 6
effect setup
effect cleanup
effect setup

পর্দায়: অর্ডার #2, অর্ডার #4, অর্ডার #6
render App
render OrderTicket, guest = 1
render OrderTicket, guest = 2
render OrderTicket, guest = 3
effect setup

পর্দায়: অর্ডার #1, অর্ডার #2, অর্ডার #3
render App
render OrderTicket, guest = 1
render OrderTicket, guest = 2
render OrderTicket, guest = 3
effect setup

পর্দায়: অর্ডার #1, অর্ডার #2, অর্ডার #3

সত্যিকারের run · Vite 8.3.5, React 19.3.1, Edge console থেকে নেওয়া · Production-এর main.tsx-এ StrictMode ছিল, তবু দ্বিগুণ হয়নি

Dev-এ বাড়তি কাজকোন ভুল ধরে
Component body, useState/useMemo/useReducer-এ দেওয়া ফাংশন দুবার ডাকাRender-এর impurity
Effect-এ setup → cleanup → setupCleanup না থাকা, যেমন সংযোগ খোলা থেকে যাওয়া
Ref callback-এ একই বাড়তি চক্রRef-এর cleanup না থাকা
পুরোনো API-র সতর্কবার্তাবাতিল হতে চলা class method

Effect কেন দুবার? বাস্তবে ব্যবহারকারী পাতা ছেড়ে যান, আবার ফেরেন। ধরুন component কথা বলার জন্য একটি chat সংযোগ খোলে কিন্তু বন্ধ করে না। প্রতিবার ফিরে আসায় একটি করে সংযোগ জমবে, আর বাগটি দেখা দেবে দুই সপ্তাহ পরে। StrictMode প্রথম মুহূর্তেই একবার বন্ধ করে আবার খোলে। Cleanup ঠিক থাকলে ব্যবহারকারী কোনো পার্থক্য টের পান না। ঠিক না থাকলে ভুলটি সঙ্গে সঙ্গে চোখে পড়ে।

“API দুবার যাচ্ছে” দেখে StrictMode মুছে ফেলা একটি পরিচিত ভুল পরামর্শ। এতে উপসর্গ লুকায়, রোগ থাকে। Production-এ বাড়তি ডাক নেই, কিন্তু ব্যবহারকারী পাতায় ফিরলে সেই effect আবার চলবে। সঠিক পথ cleanup লেখা। এই কৌশল পর্ব ১৩-এ দেখবেন।

আরও এগিয়ে থাকা পাঠকের জন্য: Rules of React আর React Compiler

React.dev-এ Rules of React তিন ভাগে লেখা। এক, component আর hook pure হবে: props ও state অপরিবর্তনীয়, JSX-এ পাঠানো মান পরে বদলানো যাবে না। দুই, React-ই component ডাকবে: আপনি PriceTag() হাতে ডাকবেন না, hook-কে মানের মতো ঘোরাবেন না। তিন, Rules of Hooks, যা আসবে পর্ব ১৪-এ।

এই নিয়ম এখন শুধু উপদেশ নয়, যন্ত্রও যাচাই করে। eslint-plugin-react-hooks-এর বর্তমান preset আমাদের ভাঙা কোড ধরে ফেলেছে:

$ npx eslint src

~/bazarghar-web\src\components\BadTotal.tsx
  3:16  error  Error: Cannot call impure function during render
…
> 3 |   const when = Date.now()
    |                ^^^^^^^^^^ Cannot call impure function
…  react-hooks/purity

~/bazarghar-web\src\components\OrderTicket.tsx
  4:3  error  Error: Cannot reassign variables declared outside of the component/hook
…
> 4 |   guest = guest + 1
    |   ^^^^^ `guest` cannot be reassigned
…  react-hooks/globals

✖ 2 problems (2 errors, 0 warnings)

সত্যিকারের run · ESLint 10.14.0, eslint-plugin-react-hooks 7.1.1, TypeScript 6.0 · ছাঁটা অংশ “…” চিহ্নিত

তবে linter সব ধরে না। এই পরীক্ষার BadTotal component-এর ২ নম্বর লাইনে ছিল prices.push(60), যেখানে prices একটি prop. এই লাইন নিয়ে কোনো error আসেনি, আর শুধু ওই লাইন রেখে চালালেও ESLint চুপ ছিল। তাই নিয়মটি আপনাকেও জানতে হবে।

React Compiler একটি build-সময়ের টুল। এটি কোড পড়ে নিজে থেকে memoization বসায়, মানে আগের ফল জমা রেখে অকারণ হিসাব কমায়। ৭ অক্টোবর ২০২৫-এ v1.0 stable হিসেবে প্রকাশ পায়। লেখার সময়ে npm-এ babel-plugin-react-compiler 1.0.0; নতুন project-এর আগে react.dev দেখে নিন, কারণ Vite plugin-এর পদ্ধতি বদলেছে। আমাদের project-এ চালিয়ে dev server থেকে PriceTag দেখলে এমন আসে:

export function PriceTag() {
    const $ = _c(2);
    …
    let t0;
    if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
        t0 = /* @__PURE__ */ _jsxDEV("p", { className: "price", children: "৳১২০" }, …);
        $[1] = t0;
    …

সত্যিকারের run · babel-plugin-react-compiler 1.0.0, @vitejs/plugin-react 6.1.2, @rolldown/plugin-babel 0.2.4 · ছাঁটা

$ হলো component-এর নিজের জমা রাখার তালিকা। একবার তৈরি JSX পরের render-এ আবার ব্যবহার হয়। Purity এখানে শর্ত: ফাংশন pure না হলে জমা করা ফল ভুল হতে পারে। Build-এ JavaScript 220.14 kB থেকে 220.84 kB হয়েছে (gzip 68.86 থেকে 69.02 kB), তবে এই খুদে অ্যাপে এটি সাধারণ ফল নয়। এই পর্বের কোডে Compiler ব্যবহার করা হবে না। Performance-এর পুরো হিসাব পর্ব ২৩-এ।

৮ · React DevTools — গাছটি চোখে দেখা

Browser-এর Elements প্যানেলে আপনি div আর article দেখেন, component দেখেন না। React DevTools extension সেই অভাব মেটায়। Chrome, Firefox আর Edge-এর store-এ আছে। অন্য browser-এর জন্য আছে react-devtools npm package. বসানোর পরে React পাতায় DevTools-এ দুটি নতুন প্যানেল আসে।

Components

Component-এর গাছ। কোনো একটিতে চাপলে তার props আর state দেখা যায়, সেখানেই মান বদলে পরীক্ষা করা যায়। “এই লেখাটি কোন component-এর?” প্রশ্নের দ্রুত উত্তর।

পাতার গঠন বুঝতে

Profiler

কোন component কতবার, কত সময়ে render হলো, তার রেকর্ড। ধীর পাতার কারণ খোঁজার টুল। ব্যবহার শিখবেন পর্ব ২৩-এ।

গতি মাপতে

Dev server চালালেই console-এ React নিজে এই পরামর্শ দেয়: Download the React DevTools for a better development experience. আমাদের স্বয়ংক্রিয় run-এ extension বসানো ছিল না। তাই গাছটি আমরা একটি script দিয়ে React-এর ভেতরের তথ্য (fiber) থেকে পড়েছি। Components প্যানেলেও গঠন এমনই দেখার কথা, শুধু চেহারা আলাদা।

App
  ProductPage
    Header
    ProductCard
      PriceTag
      StockBadge
<header><h1>বাজারঘর</h1></header>
<main>
  <article class="card">
    <h2>মিনিকেট চাল, ১ কেজি</h2>
    <p class="price">৳১২০</p>
    <span class="stock">স্টকে আছে</span>
  </article>
</main>
h
  m
  m
  m

সত্যিকারের run · Edge, Vite 8.3.5 · তৃতীয় ট্যাবটি টিকিটের উদাহরণের production build থেকে নেওয়া

তিনটি জিনিস মিলিয়ে দেখুন। এক, Fragment কোনো ছাপ রাখেনি: #root-এর সরাসরি সন্তান header আর main. দুই, component-এর গাছ আর DOM-এর গাছ এক নয়। App-এর কোনো নিজস্ব ট্যাগই নেই। তিন, production-এ নাম ছোট হয়ে h আর m হয়ে যায়, কারণ minify নাম ছোট করে। এজন্যই production-এর error বার্তা পড়তে source map লাগে। সেই আলোচনা পর্ব ৮৩-তে।

৯ · ফাইলের অভ্যাস, জোড়া দেওয়া, আর React যা করে না

মুদি দোকানের গুদামে চাল এক তাকে, তেল আরেক তাকে থাকলে নতুন কর্মচারীও জিনিস খুঁজে পান। কোডেও তাই। React কোনো ফোল্ডার-নিয়ম চাপায় না, দলের ওপর ছেড়ে দেয়। “বাজারঘর”-এর জন্য আমরা এই অভ্যাসগুলো রাখব:

  • এক ফাইলে একটি মূল component, ফাইলের নাম আর component-এর নাম এক: ProductCard.tsx.
  • components/-এ বারবার ব্যবহারের টুকরো, pages/-এ পুরো পাতা। বড় হলে ব্যবসার ভাগ ধরে সাজানো যায়, যেমন features/cart/.
  • Named export: export function PriceTag. নাম এক থাকে, খুঁজে পেতে সুবিধা। অনেক দল default export-ও ব্যবহার করে, টেমপ্লেটের App-এর মতো। এটি রুচির ব্যাপার, দলে এক রকম রাখুন।
  • Component-এর ভেতরে component ঘোষণা করবেন না। প্রতি render-এ সেটি নতুন ফাংশন হয়, ফলে React তাকে নতুন জিনিস ভাবে। কেন, তা পর্ব ১৫-এ।
  • Component হাতে ডাকবেন না: {PriceTag()} নয়, <PriceTag />.

এবার ছোট টুকরো জুড়ে একটি পাতা বানাই। এখানে সামান্য props দেখবেন: ফাংশনের parameter-এর মতো বাইরে থেকে মান আসা। খুঁটিনাটি পর্ব ৯-এ।

<div id="root"></div>
<script src="../course-kit/vendor/react-lab.js"></script>
<script type="text/jsx">
const Header = () => <header><h1>বাজারঘর</h1></header>;
const Footer = () => <footer>ঢাকা, বাংলাদেশ</footer>;
const PriceTag = ({ taka }) => <b>৳{taka}</b>;

function ProductCard({ name, taka }) {
  return <p>{name}: <PriceTag taka={taka} /></p>;
}
function ProductPage() {
  useEffect(() => {
    const kids = [...document.getElementById('root').children];
    console.log('root children:', kids.map((k) => k.tagName).join(', '));
  }, []);
  return (
    <>
      <Header />
      <main>
        <ProductCard name="চাল" taka={120} />
        <ProductCard name="ডাল" taka={140} />
      </main>
      <Footer />
    </>
  );
}
createRoot(document.getElementById('root')).render(<ProductPage />);
</script>

Console-এ আসে root children: HEADER, MAIN, FOOTER. Fragment-এর কারণে মাঝে কোনো বাড়তি div নেই। একটি ProductCard দুবার বসেছে, আলাদা মান নিয়ে। তিন নম্বর পণ্য যোগ করতে একটি লাইনই যথেষ্ট; চেষ্টা করে দেখুন। আর PriceTag-এ ৳ বদলালে সব পণ্যে বদলাবে। পরে একটি array ঘুরিয়ে এমন তালিকা বানানোর সময় প্রতিটি উপাদানে key নামের একটি মান লাগবে। সেটি পর্ব ১১-এর বিষয়, এখানে শুধু নামটি মনে রাখুন।

React যা করে না

React নিজেকে UI-এর library বলে, framework নয়। রান্নার ভালো চুলা কিনলে বাজার, ওয়েটার আর হিসাবরক্ষক আসে না। Vite দিয়ে শুরু করলে নিচের কাজগুলোর জন্য আলাদা ব্যবস্থা লাগবে।

কাজReact-এ যা আছেযা লাগবেকোথায় শিখবেন
URL অনুযায়ী পাতাকিছু নেইReact Router, TanStack Routerপর্ব ২০
Server থেকে dataEffect-এ হাতে লেখা যায়TanStack Query বা এ ধরনের টুলপর্ব ১৯
FormInput আর eventছোট form হাতে, বড় form-এ libraryপর্ব ১২, ২১
StylingclassName আর styleCSS, Tailwind বা অন্য পথপর্ব ২২
Server-এ HTML তৈরিনিচের API আছেNext.js বা এ ধরনের frameworkপর্ব ৫১–৫৫

React.dev নতুন production অ্যাপের জন্য framework-এর দিকেই ইঙ্গিত করে, আর বলে শুধু build টুল দিয়ে শুরু করলে পরে অনেক কিছু নিজেকেই জোড়া দিতে হবে। আমরা তবু Vite-এ শুরু করছি, কারণ এখানে শেখার মতো সব টুকরো আলাদা চোখে পড়ে। বাস্তবে অনেক অ্যাডমিন প্যানেল বা লগইন-এর পেছনের অ্যাপে Vite-এর SPA-ই যথেষ্ট, এ নিয়ে দলগুলোর মত আলাদা। Stack বাছাইয়ের ছক আছে পর্ব ৮৫-তে।

আরও গভীরে: Render আর commit আলাদা বলেই React ২০২২ সালের React 18 থেকে concurrent rendering দিতে পারে: জরুরি কাজ এলে অর্ধেক হিসাব থামানো বা ফেলে দেওয়া যায়, কারণ তখনও DOM ছোঁয়া হয়নি। Purity না থাকলে সেই ফেলে দেওয়া হিসাব বাইরে দাগ রেখে যেত। Element object আর আসল কাজের ইউনিট (Fiber) আলাদা জিনিস; ভেতরের গঠন পর্ব ১৫-এ।

পরীক্ষা ও ইন্টারভিউতে:

  • React library না framework? UI library; routing আর data-র জন্য বাইরের টুল বা framework লাগে।
  • Declarative UI মানে? State অনুযায়ী ফল বর্ণনা করা; DOM বদলের ধাপ React ঠিক করে।
  • Fragment কেন? বাড়তি DOM ট্যাগ ছাড়া একাধিক সন্তান ফেরত দিতে।
  • Pure component কী? একই props, state, context-এ একই ফল, আর render-এ বাইরের কিছু না বদলানো।
  • Effect dev-এ দুবার কেন? StrictMode cleanup পরীক্ষা করে; production-এ এই বাড়তি চক্র নেই।

১০ · AI-এর যুগে React শেখা

AI খুব দ্রুত component লিখে দেয়। সমস্যা হলো, ইন্টারনেটে এক দশকের পুরোনো React কোড আছে, আর AI সেখান থেকেই শিখেছে। তাই কোড চলার মতো দেখালেও version-এর সঙ্গে না-ও মিলতে পারে।

AI যা দিতে পারেসমস্যাযাচাই
ReactDOM.render(<App />, el)React 19-এ নেই। আমাদের run-এ: TS2339: Property 'render' does not existcreateRoot ব্যবহার করুন
npx create-react-app২০২৫ সালের ফেব্রুয়ারিতে নতুন অ্যাপের জন্য deprecatedreact.dev-এর পথ দেখুন
“দুবার চলছে? StrictMode মুছে দিন”ভুল লুকায়Cleanup লিখুন
Render-এ Math.random() বা বাইরের counterPurity ভাঙেESLint-এর react-hooks preset চালান
অচেনা package বা optionনাম বানানো হতে পারে, বা ঝুঁকিপূর্ণ packagenpm view আর অফিসিয়াল docs
একটিমাত্র ৫০০ লাইনের componentGod component; পরে বদলানো কঠিনগাছ আগে আপনি ঠিক করুন

AI-কে শুধু “পণ্যের পাতা বানাও” না বলে গাছ আর নিয়মটিও দিন। এতে আপনি নকশা ধরে রাখেন, AI শুধু টুকরো লেখে।

আমি React 19.3, Vite আর TypeScript ব্যবহার করছি। নিচের component গাছের জন্য শুধু ProductCard, PriceTag আর StockBadge লিখে দাও: ProductCard PriceTag StockBadge নিয়ম: ১. প্রতিটি function component, named export, আলাদা ফাইল। ২. সব component pure হবে: render-এ fetch, Date.now, Math.random বা বাইরের ভ্যারিয়েবল বদলানো চলবে না। ৩. নতুন কোনো npm package যোগ করবে না। ৪. ReactDOM.render বা class component ব্যবহার করবে না। ৫. কোডের পরে বলো, কোন অংশ react.dev-এর কোন নিয়মের সঙ্গে মিলিয়ে দেখা উচিত।

AI-এর লেখা component হাতে পেলে

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

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১২০১৩ সালে JSX দেখে অনেকে বলেছিলেন React “separation of concerns” ভেঙেছে। আজ React সবখানে। তাহলে সমালোচকরা কি ভুল ছিলেন?

পুরোপুরি ভুল নন, তবে প্রশ্নটি বদলে গিয়েছিল। তখনকার রীতি ছিল HTML এক ফাইলে, CSS আরেক ফাইলে, JavaScript তৃতীয় ফাইলে। এই ভাগকে “concern আলাদা করা” ভাবা হতো। React দল বলল, এগুলো আসলে আলাদা প্রযুক্তি, আলাদা দায়িত্ব নয়। Pete Hunt ২০১৩ সালের “React: Rethinking Best Practices” বক্তৃতায় এই যুক্তি জনপ্রিয় করেন।

একটি উদাহরণ দেখুন। “কার্টে যোগ করুন” বাটনের চেহারা, স্টক শেষ হলে বন্ধ হওয়ার যুক্তি আর ক্লিকের আচরণ: এগুলো একই দায়িত্বের অংশ। এগুলোকে তিন ফাইলে ছড়ালে একটি বদলের জন্য তিন জায়গায় যেতে হয়। Separation of concerns মানে আলাদা কারণে বদলানো জিনিস আলাদা রাখা। সেই বিচারে ভাগের রেখাটি প্রযুক্তি ধরে নয়, UI-এর অংশ ধরে টানা ভালো। এতে cohesion বাড়ে।

তবে সমালোচকদের কথাতেও সত্য ছিল। JSX-এর ভেতরে ব্যবসার হিসাব, data আনা, অনুমতি যাচাই সব ঢুকিয়ে দেওয়া সত্যিই বিপজ্জনক। পর্ব ৪-এর fat controller-এর মতো সমস্যা এখানে god component হয়ে ফেরে। তাই নিয়মটি মারা যায়নি, শুধু তার রূপ বদলেছে। দামের হিসাব একটি আলাদা ফাংশনে, data আনা একটি hook-এ, আর component-এ শুধু দেখানো: এটি এখনও আলাদা রাখাই।

ডিজাইনাররাও একটি বৈধ প্রশ্ন তোলেন: যিনি JavaScript জানেন না, তিনি কীভাবে কাজ করবেন? Vue-র single-file component আর Svelte এই প্রশ্নের অন্য উত্তর দেয়, যা পর্ব ৬৬ ও ৬৭-তে দেখবেন।

মূল উপলব্ধি: কোনো নীতির অক্ষর নয়, তার কারণটি মনে রাখুন। একসঙ্গে বদলায় এমন জিনিস কাছে রাখুন, আলাদা কারণে বদলায় এমন জিনিস দূরে রাখুন। ফাইলের extension এই বিচারের মাপকাঠি নয়।

প্র ০২StrictMode effect-কে দুবার চালায়। প্রথমে এটি কেবল বিরক্তিকর লাগে। React দল এত বড় অস্বস্তি ইচ্ছা করে রাখল কেন?

কারণ তাঁরা একটি ভবিষ্যৎ ক্ষমতার জন্য component-কে প্রস্তুত করতে চেয়েছিলেন। মার্চ ২০২২-এর React 18 ঘোষণায় বলা হয়, Strict Mode নতুন একটি dev-পরীক্ষা পেল: প্রথম mount-এর পরে React নিজে থেকে unmount করে, আগের state নিয়ে আবার mount করে। উদ্দেশ্য লেখা ছিল পরিষ্কারভাবে। ব্যবহারকারী একটি tab ছেড়ে গিয়ে ফিরে এলে React যেন আগের পর্দা সঙ্গে সঙ্গে দেখাতে পারে, আগের state রেখেই।

এর জন্য একটি শর্ত লাগে: effect বারবার তৈরি ও ধ্বংস হলেও component ভাঙবে না। অথচ বহু পুরোনো কোড ধরে নিত, “mount মানে জীবনে একবারই”। হঠাৎ এই শর্ত বদলালে অনেক অ্যাপ ভেঙে যেত। তাই React আগে dev-এ পরীক্ষা বসায়, যাতে ভাঙন ডেভেলপারের চোখে পড়ে, ব্যবহারকারীর নয়।

পরের ঘটনাটি এই সিদ্ধান্তকে অর্থ দেয়। অক্টোবর ২০২৫-এর React 19.2-তে <Activity> আসে। hidden অবস্থায় এটি সন্তানদের লুকায় আর তাদের effect unmount করে; visible হলে আবার effect mount করে। অর্থাৎ যে আচরণ তিন বছর আগে পরীক্ষা হিসেবে ছিল, তা এখন আসল বৈশিষ্ট্য।

এর পেছনে একটি বড় নকশা-ভাবনা আছে। Effect-কে “জীবনচক্রের ঘটনা” ভাবলে ভুল হয়। React.dev একে দেখতে বলে একটি সিঙ্ক্রোনাইজেশন হিসেবে: বাইরের সিস্টেমকে state-এর সঙ্গে মেলানো, শুরু আর থামা দুটিই সমান গুরুত্বের। সিঙ্ক্রোনাইজেশন যতবার খুশি চলতে পারে। এই ভাবনা পরিষ্কার হলে দুবার চলাও আর বিরক্তিকর লাগে না।

তবে খরচও আছে। ডেভেলপাররা বিভ্রান্ত হন, log দ্বিগুণ দেখেন, dev-এ API দুবার যায়। অনেকে তাই StrictMode বন্ধ করে দেন। এটি সংগত সমালোচনা: ভালো উদ্দেশ্য থাকলেও শেখার পথে এটি একটি বাধা।

মূল উপলব্ধি: ভালো টুল ভুলকে আগে, সস্তা জায়গায় ধরে। Dev-এর একটু অস্বস্তি production-এর রহস্যময় বাগের চেয়ে অনেক সস্তা।

প্র ০৩Component কত ছোট হওয়া উচিত? একই কোড দুবার দেখলেই কি আলাদা component বানাতে হবে?

না, আর এর কোনো নির্দিষ্ট সংখ্যা নেই। “৫০ লাইনের বেশি নয়” ধরনের নিয়ম সহজ, কিন্তু আসল প্রশ্ন ধরে না। আসল প্রশ্ন হলো, একটি বদল করতে কতগুলো জায়গায় যেতে হচ্ছে, আর একটি ফাইল পড়তে কত কিছু মাথায় রাখতে হচ্ছে।

দুই দিকের ঝুঁকি আছে। এক দিকে god component: একটি ফাংশন fetch, form, হিসাব, পুরো পাতা সব সামলায়। অন্য দিকে অতিরিক্ত টুকরো: PriceTagWrapper, PriceTagInner, PriceTagText, যেখানে একটি লেখা পড়তে তিনটি ফাইল খুলতে হয়। দ্বিতীয়টি দেখতে পরিষ্কার লাগে বলে বেশি বিপজ্জনক।

Sandi Metz ২০১৬ সালের “The Wrong Abstraction” লেখায় বলেন, ভুল abstraction-এর চেয়ে duplication অনেক সস্তা। ধরুন ProductCard আর OrderRow আজ দেখতে একই। আপনি একটি GenericRow বানালেন। পরের মাসে অর্ডারে স্ট্যাটাস লাগল, তারপর পণ্যে রেটিং। Component-এ isOrder, showRating, compact-এর মতো শর্ত জমতে থাকে। দুই জিনিস দেখতে এক হলেও বদলের কারণ আলাদা ছিল। এখানে দুটি আলাদা component-ই ঠিক ছিল।

Brad Frost-এর Atomic Design (২০১৩) UI-কে atom, molecule, organism ধরে ভাবতে শেখায়। এটি বড় design system-এ কাজের, তবে ছোট দলে স্তরের নাম নিয়ে তর্কই বেশি হতে পারে। React.dev-এর নিজের পরামর্শ সরল: একটি component আদর্শভাবে একটি কাজ নিয়ে থাকবে, বড় হলে ভাঙবে।

বাস্তব একটি কৌশল হলো তিন বারের নিয়ম: প্রথমবার লিখুন, দ্বিতীয়বার কপি করুন, তৃতীয়বার মিল দেখে abstraction বানান। তখন আপনি জানেন আসলে কোনটি বদলায়, কোনটি একই থাকে। ভালো নকশার অন্য আলোচনা আছে clean code পর্বে।

মূল উপলব্ধি: দেখতে মিল থাকা আর একই কারণে বদলানো এক কথা নয়। Component ভাঙুন বদলের কারণ ধরে, চেহারা ধরে নয়।

প্র ০৪Purity তো functional programming-এর তাত্ত্বিক ধারণা। একটি UI library-তে এটি এত কঠোর নিয়ম হলো কেন?

কারণ React-এর প্রায় প্রতিটি বড় ক্ষমতা এই একটি অনুমানের ওপর দাঁড়িয়ে। Component pure হলে React তাকে একটি অঙ্কের সূত্রের মতো ব্যবহার করতে পারে। সূত্র যতবার খুশি হিসাব করা যায়, মাঝপথে ফেলে দেওয়া যায়, আগের ফল জমা রাখা যায়। Component বাইরের কিছু বদলালে এর কোনোটিই নিরাপদ থাকে না।

চারটি উদাহরণ ধরা যাক।

  • Concurrent rendering: ব্যবহারকারী টাইপ করলে React অর্ধেক হওয়া একটি ভারী render ফেলে দিতে পারে। সেই render যদি বাইরের counter বাড়িয়ে থাকে, ফেলে দেওয়া কাজের দাগ থেকে যেত।
  • Memoization: একই ইনপুটে আগের ফল ফেরত দেওয়া শুধু pure ফাংশনেই ঠিক। React Compiler নিজে থেকে এটি বসায়, তাই purity এখন আরও জরুরি।
  • Server rendering: একই component server আর browser দুই জায়গায় চলে। ফল আলাদা হলে hydration mismatch হয়, যা পর্ব ৫৬-এ দেখবেন।
  • পরীক্ষা: Pure component-কে শুধু ইনপুট দিয়ে যাচাই করা যায়, বাইরের অবস্থা সাজাতে হয় না।

ধারণাটি নতুন নয়। Evan Czaplicki-র Elm ভাষা (২০১২ সালে প্রকাশ) UI-কে pure ফাংশন ধরে ভাবার একটি পরিচিত উদাহরণ। JavaScript-এর ফাংশনাল ধারণা পড়তে পারেন functional programming পর্বে। পার্থক্য হলো, Haskell বা Elm-এর ভাষাই এই নিয়ম জোর করে মানায়। JavaScript মানায় না, সে আপনার কোডে যেকোনো ভ্যারিয়েবল বদলাতে দেয়। তাই React-কে StrictMode আর lint rule দিয়ে পরে যাচাই করতে হয়।

এখানেই একটি সীমা। আমাদের নিজের run-এ দেখলেন, props-এ push করা linter ধরেনি। ভাষা যা নিশ্চিত করে না, টুল তার সব ধরতে পারে না। নিয়মটি শেষ পর্যন্ত মানুষের দায়িত্ব।

মূল উপলব্ধি: Purity কোনো শুদ্ধতার শখ নয়। এটি React আর আপনার মধ্যে একটি চুক্তি: আপনি হিসাব পরিষ্কার রাখবেন, React তার বদলে সময়সূচি, cache আর গতির সিদ্ধান্ত নিজে নেবে।

প্র ০৫Create React App-কে বিদায় দেওয়া হলো, React.dev framework-এর দিকে ইঙ্গিত করে। তাহলে Vite দিয়ে সাধারণ React অ্যাপ বানানো কি ভুল?

ভুল নয়, তবে প্রশ্নটি একটু ভেঙে দেখা দরকার। ১৪ ফেব্রুয়ারি ২০২৫-এ React দল Create React App-কে নতুন অ্যাপের জন্য deprecated ঘোষণা করে। তাঁদের যুক্তি ছিল দুটি: টুলটির সক্রিয় রক্ষণাবেক্ষণকারী নেই, আর routing, data fetching ও code splitting-এর মতো সমস্যা framework-গুলো আগেই মিটিয়েছে। পরামর্শ ছিল, অ্যাপে routing লাগলে framework থেকে আপনি সম্ভবত উপকার পাবেন। যাঁদের বিশেষ চাহিদা আছে বা যাঁরা React শিখতে চান, তাঁদের জন্য Vite, Parcel, Rsbuild-এর কথাও সেখানে আছে।

এই অবস্থানের পেছনে একটি বাস্তব অভিজ্ঞতা আছে। Vite-এর SPA শুরুতে সরল। কিন্তু পরে আপনি router যোগ করেন, data আনার ব্যবস্থা করেন, loading state সামলান, code split করেন, SEO দরকার হলে server rendering খোঁজেন। React.dev-এর কথায়, এটি প্রায়ই নিজের একটি অস্থায়ী framework বানানোর সমান।

আবার অন্য দিকের যুক্তিও শক্ত। Framework নিজের জটিলতা আনে: server চালানোর খরচ, আরও নিয়ম, দ্রুত বদলানো API, আর কিছু ক্ষেত্রে নির্দিষ্ট hosting-এর দিকে ঝোঁক (lock-in). লগইনের পেছনের অ্যাডমিন প্যানেলে SEO লাগে না, প্রথম পাতার গতিও ততটা জরুরি নয়। সেখানে dist/ ফোল্ডার যেকোনো static hosting-এ রেখে দেওয়ার সরলতা বড় সুবিধা। React দল নিজেও জানায় যে framework দিয়েও CDN বা static hosting-এ SPA রাখা যায়, তাই “framework মানেই server” কথাটিও ঠিক নয়।

সম্প্রদায়ে এই প্রশ্নে মতভেদ আছে, আর তা স্বাভাবিক। একটি কাজের নিয়ম হলো জিজ্ঞেস করা: পাতা কি সার্চ ইঞ্জিনে আসতে হবে? প্রথমবার আসা ব্যবহারকারীর গতি কি বিক্রির সঙ্গে জড়িত? দল কি একটি server চালাতে প্রস্তুত? “বাজারঘর”-এর দোকানের সামনের অংশে উত্তর “হ্যাঁ”, তাই পর্ব ৫১ থেকে Next.js আসবে। আর অ্যাডমিন অংশে Vite-ও যথেষ্ট হতে পারত।

মূল উপলব্ধি: টুলের নাম নয়, প্রয়োজন থেকে শুরু করুন। React ভালো করে শিখলে আপনি যেকোনো framework-এ দাঁড়াতে পারবেন, কারণ সবার নিচে সেই একই component, render আর purity.

অনুশীলন

প্রথমে নিজে করুন, তারপর উত্তর মিলিয়ে নিন।

  1. গাছের হিসাব (numerical): ছবি ৭.১-এর গাছ নিন। ProductDetails-এ একটি PriceTag ও একটি StockBadge আছে, আর RelatedProducts-এ চারটি ProductCard, প্রতিটিতে একটি PriceTag ও একটি StockBadge. (ক) কত রকম component? (খ) পাতায় মোট কতটি বসেছে? (গ) StrictMode-সহ dev-এ প্রথম render-এ মোট কতবার component ফাংশন চলবে? (ঘ) Production-এ?

    (ক) ১৪ রকম: ProductPage, Header, Logo, SearchBox, CartButton, ProductDetails, ProductGallery, ProductInfo, PriceTag, StockBadge, AddToCart, RelatedProducts, ProductCard, Footer.

    (খ) ২৫টি: ProductPage ১ + Header অংশ ৪ (নিজে + ৩) + ProductDetails অংশ ৬ (নিজে + Gallery + Info + PriceTag + StockBadge + AddToCart) + RelatedProducts অংশ ১৩ (নিজে + ৪ × ৩ = ১২) + Footer ১ = ১ + ৪ + ৬ + ১৩ + ১ = ২৫.

    (গ) ২৫ × ২ = ৫০ বার। (ঘ) ২৫ বার। PriceTag-এর নিয়ম বদলালে কিন্তু একটি ফাইলই বদলাতে হবে, যদিও সেটি পাতায় ৫ জায়গায় আছে (১ + ৪)।

  2. নকশা সমালোচনা: একজন ফ্রিল্যান্সারের ShopPage.tsx ৬০০ লাইনের। এতে render-এর সময়েই fetch ডাকা হয়, বাইরের let cartCount বাড়ানো হয়, দামের হিসাব চারবার কপি করা, প্রতিটি পণ্যের কার্ড হাতে লেখা, আর main.tsx থেকে StrictMode মুছে দেওয়া হয়েছে কারণ “API দুবার যাচ্ছিল”। অন্তত চারটি সমস্যা ও সমাধান লিখুন।
    • God component: Header, পণ্যের তালিকা, কার্ট আলাদা component-এ ভাঙুন; গাছ আগে কাগজে আঁকুন।
    • Render-এ fetch: এটি side effect. প্রতি render-এ নতুন অনুরোধ যাবে। পর্ব ১৩ ও ১৯-এর পথে সরান।
    • বাইরের cartCount: Purity ভাঙে, আর এটি বদলালে পর্দাও বদলাবে না। State লাগবে (পর্ব ১০)।
    • কপি করা দামের হিসাব: একটি PriceTag বা formatTaka ফাংশনে আনুন, যেন একটি বদল সব জায়গায় পৌঁছায়।
    • হাতে লেখা কার্ড: একটি ProductCard, data ঘুরিয়ে তালিকা (পর্ব ১১)।
    • StrictMode মোছা: সতর্কবার্তা বন্ধ করেছে, ভুল নয়। ফিরিয়ে আনুন, আসল কারণ ঠিক করুন।
  3. আউটপুট অনুমান: let count = 0; function Visit() { count = count + 1; return <p>{count}</p>; }. App দুটি <Visit /> ফেরত দেয়, সঙ্গে একটি useEffect আছে যা console.log('on') করে আর cleanup-এ console.log('off'). (ক) StrictMode-সহ dev build-এ পর্দায় কী আসবে, আর effect-এর log কী? (খ) Production build-এ?

    (ক) প্রতিটি Visit দুবার চলে, আর দ্বিতীয় ডাকের ফলটি পর্দায় যায়। প্রথম Visit-এ count হয় 1, তারপর 2; দ্বিতীয় Visit-এ 3, তারপর 4. তাই পর্দায় 2 আর 4. Effect log: on, off, on.

    (খ) পর্দায় 1 আর 2; log শুধু on. নিজে npm run dev আর npm run preview চালিয়ে মিলিয়ে নিন। উত্তর আলাদা, অথচ কোড এক: এটিই impurity-র লক্ষণ।

  4. নিজের project (কমান্ড): অংশ ৪-এর project বানান। Footer নামে একটি component যোগ করুন, ProductPage-এর Fragment-এ বসান। তারপর npm run build চালান, আর React DevTools-এর Components প্যানেলে গাছ দেখুন। Build-এর আউটপুটে কী কী বদলাবে, আর গাছে Footer কোথায় আসবে?

    আমাদের সত্যিকারের run-এ 21 modules হলো 22 modules, কারণ একটি নতুন ফাইল যোগ হয়েছে। JavaScript ফাইল index-CyGVGsQH.js থেকে হলো index-B7eK06Tn.js, কারণ বিষয়বস্তু বদলেছে। CSS বদলায়নি, তাই index-DJjl32jt.css নামটি একই রইল। আকার 220.14 kB থেকে 220.25 kB. গাছে Footer বসবে ProductPage-এর সরাসরি সন্তান হিসেবে, Header-এর ভাইবোন। ProductPage-এর কোনো নিজস্ব DOM ট্যাগ দেখা যাবে না, কারণ Fragment.

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমVite-এর একটি React অ্যাপে প্রথম পর্দা আসার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Browser HTML পড়ে, #root তখনো খালি
  2. createRoot ঘরটির দায়িত্ব নেয়
  3. root.render বর্ণনাটি React-কে জানায়
  4. React component ফাংশনগুলো ডাকে (render)
  5. React আসল DOM বদলায় (commit)
  6. useEffect-এর কাজ চলে

একটি JSX-এ <productCard /> লেখা হলো, যেখানে productCard একটি ফাংশন। TypeScript যাচাই ছাড়া dev server-এ চালালে কী হবে?

  • ফাংশনটি ঠিকমতো চলবে
  • Dev server শুরুই হবে না
  • অচেনা HTML ট্যাগ বসবে, ফাংশন চলবে না, console-এ সতর্কবার্তা আসবে
  • React নিজে থেকে নামটি ProductCard করে নেবে

একটি component-এ পাশাপাশি header আর main ফেরত দিতে হবে, আর DOM-এ কোনো বাড়তি ট্যাগ রাখা যাবে না। কোনটি ঠিক?

  • খালি ট্যাগ <>…</> দিয়ে মোড়ানো
  • একটি div দিয়ে মোড়ানো
  • দুটি return লেখা
  • কোনো মোড়ক ছাড়াই পাশাপাশি লেখা

নিচের কোন কাজটি একটি component-এর render-এর ভেতরে করা নিরাপদ?

  • ফাংশনের বাইরের একটি counter বাড়ানো
  • পাওয়া props-এর array-তে push করা
  • render-এর ভেতরেই fetch ডাকা
  • এই render-এই তৈরি করা নতুন array-তে push করা

StrictMode চালু একটি React 19 অ্যাপে dev-এ একটি useEffect-এর log এলো: setup, cleanup, setup. এর সঠিক ব্যাখ্যা কোনটি?

  • এটি React-এর একটি বাগ, পরের version-এ ঠিক হবে
  • StrictMode dev-এ cleanup পরীক্ষা করছে; production-এ এই বাড়তি চক্র নেই
  • Production-এও প্রতিটি effect দুবার চলবে
  • StrictMode মুছে ফেলাই সঠিক সমাধান

let n = 0; function T() { n = n + 1; return <p>{n}</p>; }. পাতায় তিনটি <T /> আছে, StrictMode-সহ dev build. প্রথম পর্দায় কী দেখা যাবে?

  • 1, 2, 3
  • 1, 3, 5
  • 2, 4, 6
  • 6, 6, 6

React দিয়ে Vite-এ একটি অ্যাপ শুরু করলে নিচের কোনটি নিজে থেকে পাওয়া যায় না?

  • URL অনুযায়ী পাতা বদলানোর routing
  • Function component
  • useState hook
  • StrictMode

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব শব্দ এখানে একসাথে:

মনে রাখুন

  1. React-এর কাজ পর্দাকে data-র সঙ্গে মিলিয়ে রাখা: UI = f(state).
  2. পাতা ভাঙুন এক কাজ, আবার ব্যবহার, আলাদা বদলের কারণ ধরে।
  3. Component PascalCase ফাংশন, একটি root ফেরত দেয়; বাড়তি ট্যাগ না চাইলে Fragment.
  4. Render হিসাব, commit বদল। createRoot ঘর নেয়, render অনুরোধ জানায়, ফাংশন চলার পরে DOM বদলায়।
  5. Component pure রাখুন। StrictMode dev-এ দুবার চালিয়ে ভুল ধরে, production-এ চলে না; সেটি মুছবেন না।

পরের পর্বে: এই পর্বে JSX ব্যবহার করেছেন, কিন্তু খোলাসা করিনি। পর্ব ৮-এ দেখবেন JSX আসলে কীতে বদলায়, className কেন, আর {…}-এর ভেতরে কী লেখা যায়।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান