Keeping Components Pure

প্রতিটি কম্পোনেন্টকে Pure বা খাঁটি রাখা

কিছু জাভাস্ক্রিপ্ট ফাংশনকে pure বা খাঁটি বলা হয়। Pure ফাংশন শুধু হিসাব-নিকাশ (calculation) করে এবং তার বাইরের কোনো কিছু পরিবর্তন করে না। তুমি যদি তোমার কম্পোনেন্টগুলোকে কঠোরভাবে pure function হিসেবে লেখো, তাহলে কোডবেস বড় হওয়ার সাথে সাথে অনেক অপ্রত্যাশিত বাগ এবং অদ্ভুত আচরণ থেকে বাঁচতে পারবে। এর জন্য তোমাকে কয়েকটি নিয়ম মেনে চলতে হবে।

তুমি এই পাঠে শিখবে:

  • Purity বা বিশুদ্ধতা কী এবং এটি কীভাবে তোমাকে বাগ এড়াতে সাহায্য করবে।
  • কম্পোনেন্ট রেন্ডার হওয়ার সময় কীভাবে সেটিকে pure রাখা যায়।
  • Strict Mode ব্যবহার করে কীভাবে কম্পোনেন্টের ভুলগুলো খুঁজে বের করা যায়।

পিউরিটি: কম্পোনেন্ট যখন গণিতের সূত্র

কম্পিউটার বিজ্ঞানে, একটি pure function-এর দুটি প্রধান বৈশিষ্ট্য থাকে:

  1. সে শুধু নিজের কাজটাই করে: ফাংশনটি কল করার আগে থেকে বিদ্যমান Global অবজেক্ট বা ভ্যারিয়েবলকে সে পরিবর্তন করে না।
  2. একই ইনপুটে, একই আউটপুট: একই ইনপুট দিলে, একটি pure function সবসময় একই ফলাফল রিটার্ন করবে।

তুমি হয়তো গণিতের সূত্রের সাথে এর মিল খুঁজে পাবে। যেমন: y=2xy = 2x

  • যদি x=2x = 2 হয়, তাহলে yy সবসময় 44 হবে।
  • যদি x=3x = 3 হয়, তাহলে yy সবসময় 66 হবে।

y=2xy = 2x সূত্রে x=3x=3 হলে, দিনের কোন সময় শেয়ার বাজারের অবস্থা কী, তার উপর নির্ভর করে yy-এর মান কখনো 99 বা 1-1 হবে না। এটি সবসময় 66-ই হবে।

জাভাস্ক্রিপ্টে এই ফাংশনটি দেখতে এমন হবে:

function double(number) {
  return 2 * number;
}

double একটি pure function। তুমি একে 3 পাস করলে এটি সবসময় 6 রিটার্ন করবে।

React এই ধারণার উপর ভিত্তি করেই ডিজাইন করা হয়েছে। React ধরে নেয় যে তোমার লেখা প্রতিটি কম্পোনেন্ট একটি pure function। এর মানে হলো, তোমার লেখা React কম্পোনেন্টকে একই ইনপুট (props) দিলে, তাকে সবসময় একই JSX রিটার্ন করতে হবে।

function Recipe({ drinkers }) {
  return (
    <ol>
      <li>Boil {drinkers} cups of water.</li>
      <li>
        Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.
      </li>
      <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
    </ol>
  );
}
 
export default function App() {
  return (
    <section>
      <h1>Spiced Chai Recipe</h1>
      <h2>For two</h2>
      <Recipe drinkers={2} />
      <h2>For a gathering</h2>
      <Recipe drinkers={4} />
    </section>
  );
}

যখন তুমি Recipe কম্পোনেন্টে drinkers={2} পাস করবে, এটি সবসময় 2 cups of water যুক্ত JSX রিটার্ন করবে। ঠিক যেমন একটি গণিতের সূত্র।


Side Effects: অপ্রত্যাশিত ফলাফল

React-এর রেন্ডারিং প্রক্রিয়া সবসময় pure হতে হবে। কম্পোনেন্টগুলোর কাজ শুধু তাদের JSX রিটার্ন করা, রেন্ডারিংয়ের আগে থেকে বিদ্যমান কোনো অবজেক্ট বা ভ্যারিয়েবল পরিবর্তন করা নয়। যদি কোনো কম্পোনেন্ট এমনটি করে, তবে তাকে impure বা অবিশুদ্ধ বলা হয়।

নিচের কম্পোনেন্টটি এই নিয়মটি ভঙ্গ করছে:

let guest = 0;
 
function Cup() {
  // খারাপ অভ্যাস: আগে থেকে তৈরি ভ্যারিয়েবল পরিবর্তন করা!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}
 
export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

এই কম্পোনেন্টটি তার বাইরে থাকা guest ভ্যারিয়েবলকে পড়ছে এবং পরিবর্তনও করছে। এর মানে হলো, এই কম্পোনেন্টটিকে একাধিকবার কল করলে ভিন্ন ভিন্ন JSX তৈরি হবে! এটি খুবই অপ্রত্যাশিত এবং এর কারণে অদ্ভুত সব বাগ তৈরি হতে পারে।

এই সমস্যাটি সমাধান করার জন্য guest-কে prop হিসেবে পাস করতে হবে:

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}
 
export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

এখন কম্পোনেন্টটি pure, কারণ এটি যে JSX রিটার্ন করে, তা শুধু guest prop-এর উপর নির্ভরশীল। রেন্ডারিং হলো একটি পরীক্ষার মতো: প্রতিটি কম্পোনেন্টের উচিত নিজের কাজ নিজে করা!

StrictMode দিয়ে অবিশুদ্ধ হিসাব শনাক্ত করা

React-এ "Strict Mode" নামে একটি বিশেষ ব্যবস্থা আছে, যা ডেভেলপমেন্টের সময় প্রতিটি কম্পোনেন্টের ফাংশন দুইবার করে কল করে। দুইবার কল করার মাধ্যমে, Strict Mode সেই সব কম্পোনেন্ট খুঁজে বের করতে সাহায্য করে যারা বিশুদ্ধতার নিয়ম ভঙ্গ করে।

আগের অবিশুদ্ধ উদাহরণে, আউটপুট আসছিল "Guest #2", "Guest #4", "Guest #6"। কারণ ফাংশনটি impure হওয়ায় দুইবার কল করা হলে এর মান অপ্রত্যাশিতভাবে বেড়ে যাচ্ছিল। কিন্তু pure ফাংশন শুধু হিসাব করে, তাই এদের দুইবার কল করলেও ফলাফলের কোনো পরিবর্তন হয় না।

Strict Mode প্রোডাকশনে কোনো প্রভাব ফেলে না, তাই এটি অ্যাপকে স্লো করে না। <React.StrictMode> দিয়ে তোমার রুট কম্পোনেন্টকে র‍্যাপ করে তুমি এটি চালু করতে পারো।

লোকাল মিউটেশন (Local mutation): কম্পোনেন্টের নিজস্ব গোপন ব্যাপার

আগের উদাহরণে সমস্যা ছিল কারণ কম্পোনেন্টটি রেন্ডারিংয়ের সময় আগে থেকে তৈরি একটি ভ্যারিয়েবল পরিবর্তন করছিল। এই পরিবর্তন করাকে মিউটেশন (mutation) বলে।

তবে, রেন্ডারিংয়ের সময় নতুন তৈরি করা ভ্যারিয়েবল বা অবজেক্ট পরিবর্তন করা সম্পূর্ণ ঠিক আছে। নিচের উদাহরণে cups অ্যারেটি TeaGathering ফাংশনের ভেতরেই তৈরি হয়েছে। তাই এটিকে পরিবর্তন করলেও বাইরের কেউ প্রভাবিত হচ্ছে না। একে বলে লোকাল মিউটেশন—এটা যেন তোমার কম্পোনেন্টের ছোট্ট গোপন ব্যাপার।

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}
 
export default function TeaGathering() {
  let cups = [];
  for (let i = 1; i <= 12; i++) {
    cups.push(<Cup key={i} guest={i} />);
  }
  return cups;
}

কখন Side Effect ঘটানো যেতে পারে?

প্রোগ্রামিংয়ের আসল উদ্দেশ্যই হলো কোনো না কোনো পরিবর্তন ঘটানো। স্ক্রিন আপডেট করা, অ্যানিমেশন শুরু করা, ডেটা পরিবর্তন করা—এগুলোকে বলে সাইড এফেক্ট (Side Effects)। এগুলো রেন্ডারিংয়ের সময় ঘটে না, বরং "সাইডে" ঘটে।

React-এ, সাইড এফেক্টগুলো সাধারণত ইভেন্ট হ্যান্ডলারের (event handlers) ভেতরে থাকে। ইভেন্ট হ্যান্ডলার হলো সেই সব ফাংশন যা কোনো অ্যাকশনের ফলে রান হয়—যেমন, বাটনে ক্লিক করলে। যদিও ইভেন্ট হ্যান্ডলারগুলো কম্পোনেন্টের ভেতরেই লেখা হয়, এরা রেন্ডারিংয়ের সময় রান হয় না! তাই ইভেন্ট হ্যান্ডলারকে pure হতে হয় না।

যদি তুমি কোনো সঠিক ইভেন্ট হ্যান্ডলার খুঁজে না পাও, তাহলে শেষ বিকল্প হিসেবে useEffect (opens in a new tab) ব্যবহার করতে পারো। এটি React-কে বলে যে, রেন্ডারিং শেষ হওয়ার পরে সাইড এফেক্টটি রান করতে।

সার সংক্ষেপ

  • একটি কম্পোনেন্টকে অবশ্যই pure হতে হবে, অর্থাৎ:
    • সে শুধু নিজের কাজটাই করবে। রেন্ডারিংয়ের আগে থেকে বিদ্যমান কোনো ভ্যারিয়েবল বা অবজেক্ট সে পরিবর্তন করবে না।
    • একই ইনপুটে, একই আউটপুট। একই props দিলে, একটি কম্পোনেন্ট সবসময় একই JSX রিটার্ন করবে।
  • স্ক্রিন আপডেট করার জন্য স্টেট (state) সেট করতে হয়, আগে থেকে থাকা অবজেক্ট পরিবর্তন করা বা মিউটেট করা উচিত নয়।
  • যখন কোনো কিছু পরিবর্তন করার দরকার হবে, তখন সেটি ইভেন্ট হ্যান্ডলারের মাধ্যমে করাই শ্রেয়।

তোমার জন্য চ্যালেঞ্জ 🧗

১. একটি ভাঙা ঘড়ি ঠিক করো

এই Clock কম্পোনেন্টটি মধ্যরাত থেকে সকাল ৬টা পর্যন্ত "night" ক্লাস এবং অন্য সময়ে "day" ক্লাস সেট করার চেষ্টা করছে। কিন্তু এটি সরাসরি DOM পরিবর্তন করার কারণে কাজ করছে না। রেন্ডারিং একটি হিসাব, এর কাজ কোনো কিছু "করা" নয়। তুমি কি কম্পোনেন্টটিকে pure রেখে একই কাজ করতে পারো?

export default function Clock({ time }) {
  let hours = time.getHours();
  if (hours >= 0 && hours <= 6) {
    document.getElementById("time").className = "night";
  } else {
    document.getElementById("time").className = "day";
  }
  return <h1 id="time">{time.toLocaleTimeString()}</h1>;
}
import { useState, useEffect } from "react";
import Clock from "./Clock.js";
 
function useTime() {
  const [time, setTime] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => {
      setTime(new Date());
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return time;
}
 
export default function App() {
  const time = useTime();
  return <Clock time={time} />;
}
body > * {
  width: 100%;
  height: 100%;
}
.day {
  background: #fff;
  color: #222;
}
.night {
  background: #222;
  color: #fff;
}

© 2025 React JS Bangla Tutorial.