ReactNext
React-documentationBasics-2

2.7 Objects in a State

Welcome to React Next Documentation Bangla

Updating Objects in a State

রিয়াক্টে যেকোন ধরনের জাভাস্ক্রিপ্ট ভ্যালুকে তার স্টেট হিসেবে নিতে পারে। কিন্তু যখনি তুমি রিয়াক্টের স্টেট হিসেবে কোন জাভাস্ক্রিপ্ট অবজেক্ট ব্যবহার করো, তা কখনওই তুমি সরাসরি চেঞ্জ করতে পারবে না বা মিউটেট করতে পারবে না। এই পরিস্থিতিতে তোমাকে সম্পূর্ণ নতুন অবজেক্ট দিয়ে আগের অবজেক্টকে রিপ্লেস করে দিতে হবে, নাহলে আগের অবজেক্ট থেকে একটা কপি বানিয়ে সেই কপি অবজেক্ট দিয়ে আগের স্টেটের অবজেক্টকে রিপ্লেস করে দিতে হবে। অর্থাৎ, যেভাবেই করো, তোমাকে সবসময় স্টেট আপডেট করার সময় নতুন ভ্যালু দিয়ে আগের ভ্যালুকে রিপ্লেস করে দিতে হবে।

(What is a Mutation) মিউটেশন কি?

মিউটেশন মানে হলো পরিবর্তন করা। জাভাস্ক্রিপ্টের ভাষায় কোনো ভেরিয়েবলের ভ্যালুকে সরাসরি পরিবর্তন করে ফেলাকে মিউটেশন বলা হয়। রিয়াক্ট স্টেটের ভ্যালুকে সরাসরি পরিবর্তন করাকে নিরুৎসাহিত করে। রিয়াক্টে স্টেট হিসেবে যেকোন ভ্যালু স্টোর করা যায়, যেমনঃ string, number, Boolean, Array, Object

string, number, Boolean হলো জাভাস্ক্রিপ্টের প্রিমিটিভ ভ্যালু, যা read-only, অর্থাৎ এগুলো কখনো পরিবর্তন করা যায় না।

অন্যদিকে Array, Object হলো রেফারেন্স ভ্যালু। যা টেকনিক্যালি পরিবর্তন করা গেলেও রিয়াক্ট স্টেট-এর ভ্যালুতে সরাসরি রেফারেন্স ভ্যালুকে পরিবর্তন করতে নিষেধ করে। রিয়াক্টে স্টেট আপডেটের ক্ষেত্রে এসব ভ্যালুকেও read-only হিসেবে চিন্তা করতে বলে।

চলুন একটু উদাহরণের মাধ্যমে বিস্তারিত বুঝি:

const [x, setX] = useState(0);

এখানে স্টেট ভ্যারিএবল x এর ভ্যালু হিসেবে 0 রাখা হয়েছে। 0 হলো একটি প্রিমিটিভ ভ্যালু।

setX(5);

এখানে setX করে স্টেট ভ্যারিএবল x এর ভ্যালু 5 করা হয়েছে। এখানে x এর ভ্যালু 0 কে কিন্তু চেঞ্জ করা হয়নি, কেননা এটা হলো একটা প্রিমিটিভ ভ্যালু, যা কখনো চেঞ্জ করা যায় না বা read-only, বরং 5, যা একটি নতুন ভ্যালু, তা দিয়ে আগের ভ্যালুকে রিপ্লেস করা হয়েছে।

কিন্তু যখন আমাদের স্টেটের ভ্যালু এমন অবজেক্ট হবে,

const [position, setPosition] = useState({ x: 0, y: 0 });

যদিও এটা টেকনিক্যালি position.x = 5; এভাবে পরিবর্তন করা সম্ভব, কিন্তু এটা করলে তা মেইন অবজেক্টকে মিউটেড করে। যা রিয়াক্ট কখনও রিকমেন্ড করেনা। তোমাকে অবশ্যই স্টেট চেঞ্জ করার সময় সম্পূর্ণ নতুন ভ্যালু বা নতুন অবজেক্ট দিয়ে আগের ভ্যালুকে রিপ্লেস করে দিতে হবে।

তাহলে কিভাবে করবো, চলুন জেনে নেই।

(Treat state as read-only) রিয়াক্টের স্টেটকে read-only চিন্তা করতে হবে।

import { useState } from "react";
 
export default function MovingDot() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0,
  });
  return (
    <div
      onPointerMove={(e) => {
        position.x = e.clientX;
        position.y = e.clientY;
      }}
      style={{
        position: "relative",
        width: "100vw",
        height: "100vh",
      }}
    >
      <div
        style={{
          position: "absolute",
          backgroundColor: "red",
          borderRadius: "50%",
          transform: `translate(${position.x}px, ${position.y}px)`,
          left: -10,
          top: -10,
          width: 20,
          height: 20,
        }}
      />
    </div>
  );
}

উপরের কোডে একটা পয়েন্টার বানানো হয়েছে, যা মাউস মুভ করার সাথে সাথে মাউসকে ফলো করে মুভ করার কথা। কিন্তু কোডটা রান করলে দেখা যাবে কোডটা কাজ করছে না।

কাজ করছে না কারণ কোডের এই অংশে একটু ভুল আছে।

    onPointerMove={e => {
      position.x = e.clientX;
      position.y = e.clientY;
    }}

এখানে onPointerMove event listener এ স্টেট ভ্যারিএবল position এর ভ্যালু একবার স্ক্রিনে রেন্ডার হয়ে যাওয়ার পর আবার সেই ভ্যালুকেই সরাসরি পরিবর্তন করা হয়েছে, এজন্যই কাজ করছে না। কেননা চেঞ্জটা কোন setter function এ করা হচ্ছে না, তাই রিয়াক্ট জানতেই পারছে না যে স্টেটের ভ্যালু চেঞ্জ হয়েছে এবং তাকে রি-রেন্ডার করতে হবে।

এক্ষেত্রে এটা চেঞ্জ করতে হলে setter function এর ভিতরে চেঞ্জ করতে হবে, যাতে ভ্যালু চেঞ্জ হলেই রি-রেন্ডার ট্রিগার হয়ে যায়।

onPointerMove={(e) => {
    setPosition({
        x: e.clientX,
        y: e.clientY,
    });
}};

খেয়াল করে দেখো, এখানে কিন্তু আগের position অবজেক্টের কোন ভ্যালুকে চেঞ্জ করা হয়নি, সম্পূর্ণ নতুন একটা অবজেক্ট বানিয়ে আগের অবজেক্টকে রিপ্লেস করে দেওয়া হয়েছে।

লোকাল মিউটেশন হলে সেটা ঠিক আছে।

এভাবে কোড করা যাবে না, যা স্টেটের বর্তমান অবজেক্ট position কেই সরাসরি চেঞ্জ করে।

position.x = e.clientX;
position.y = e.clientY;

কিন্তু যদি এভাবে কোড করি, যাতে আমরা সম্পূর্ণ নতুন একটা অবজেক্ট বানিয়ে আগের স্টেট ভ্যালুর অবজেক্টকে রিপ্লেস করে দিই, তাহলে সেটা সম্পূর্ণভাবে ঠিক আছে।

    setPosition((e) => {
        x: e.clientX,
        y: e.clientY;
    });

আবার একটা লোকাল অবজেক্ট ভ্যারিয়েবল বানিয়ে সেটা দিয়েও চেঞ্জ করতে পারি, যেমনঃ

const nextPosition = {};
nextPosition.x = e.clientX;
nextPosition.y = e.clientY;
 
setPosition(nextPosition);

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

মিউটেশন তখনই প্রবলেম যখন তুমি কোন স্টেটের এক্সিস্টিং অবজেক্টে সরাসরি পরিবর্তন করো, তা নাহলে তুমি লোকাল ভেরিয়েবল পরিবর্তন করতে পারো, এতে কোন সমস্যা নেই।

Copying objects with the spread syntax

আগের উদাহরণে আমরা যেমন দেখলাম যে যখনি আমরা স্টেটের অবজেক্ট ভ্যালু পরিবর্তন করবো, তখন আমাদের একটা নতুন অবজেক্ট স্টেটে দিতে হবে। তার মানে আমাদের প্রতিবার একটা করে ফ্রেশ অবজেক্ট বানাতে হচ্ছে।

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

এক্ষেত্রে আমরা আগের অবজেক্টের একটা কপি বানিয়ে নিয়ে শুধুমাত্র যেই যেই ভ্যালু পরিবর্তন করতে চাই, সেগুলো পরিবর্তন করে দিলেই তো ঝামেলা শেষ।

এই কাজটা করতেই জাভাস্ক্রিপ্টের ... spread অপারেটর আমাদের হেল্প করে থাকে। ... spread অপারেটর ব্যবহার করে আমরা আগের অবজেক্টের একটা Shallow কপি বানিয়ে নিতে পারি। তবে মনে রাখতে হবে ... spread অপারেটর নেস্টেড অবজেক্টের ক্ষেত্রে শুধুমাত্র এক লেভেল কপি করে। যদি ডিপলি কপি করতে চাই, তাহলে ... spread অপারেটর একাধিকবার ব্যবহার করতে হবে।

ধরুন আমাদের এই অবজেক্ট স্টেট ভ্যারিয়েবলের firstName প্রপার্টির ভ্যালু চেঞ্জ করা লাগবে।

const [person, setPerson] = useState({
  firstName: "John",
  lastName: "Doe",
  age: 30,
});
 
const handleChangeFirstName = (newFirstName) => {
  // পূর্বের অবজেক্টটি কপি করে একটি নতুন অবজেক্ট তৈরি করা হচ্ছে
  setPerson((prevPerson) => ({
    ...prevPerson, // পূর্বের গুণাবলীর জন্য স্প্রেড অপারেটর
    firstName: newFirstName, // শুধু firstName গুণাবলীর মান আপডেট করা
  }));
};
 
// ব্যবহার উদাহরণ
handleChangeFirstName("Jane");

কোডের ব্যাখ্যা

এই কোড স্নিপেটে, তোমার কাছে person নামের একটি স্টেট ভেরিয়েবল আছে, যা firstName, lastName, এবং age এর মত গুণাবলী ধারণ করে। যখন তুমি firstName পরিবর্তন করতে চাও, তখন তুমি সরাসরি মূল অবজেক্টটি পরিবর্তন করার পরিবর্তে স্প্রেড অপারেটর (...prevPerson) ব্যবহার করে বিদ্যমান অবজেক্টের একটি শ্যালো কপি তৈরি কর। তারপর, তুমি সহজেই নতুন মান সহ firstName গুণাবলীর মান আপডেট করো।

এই পদ্ধতি তোমাকে স্টেটের অম্যুতাবিলিটি (immutability) বজায় রাখতে সাহায্য করে এবং নিশ্চিত করে যে React পরিবর্তনটি জানে, যা পুনরায় রেন্ডারিং ট্রিগার করে।

কেন এটি গুরুত্বপূর্ণ?

React-এ অম্যুতাবিলিটি বজায় রাখা কিছু কারণে খুবই গুরুত্বপূর্ণ:

  1. পারফরম্যান্স: যখন React নির্ধারণ করে যে একটি কম্পোনেন্ট পুনরায় রেন্ডার করতে হবে কিনা, তখন এটি বর্তমান স্টেটের সাথে নতুন স্টেট তুলনা করে। যদি তুমি বিদ্যমান স্টেটটি পরিবর্তন কর, তাহলে React পরিবর্তনটি সনাক্ত নাও করতে পারে, যার ফলে সম্ভাব্য বাগ বা পারফরম্যান্সের সমস্যা হতে পারে।

  2. প্রিডিকটেবিলিটি: অম্যুতাবিলিটি তোমাকে পূর্ববর্তী গুণাবলীর তুলনায় নতুন গুণাবলীর মান বোঝার জন্য আরো সাহায্য করে। নতুন ইনস্ট্যান্স তৈরি করলে এটি ট্র্যাক করা সহজ হয়।

  3. ডিবাগিং: যখন তুমি Redux বা React DevTools এর মতো টুল ব্যবহার কর, তখন অম্যুতাবিল স্টেটের সাথে অ্যাপ্লিকেশন ডিবাগ করা অনেক সহজ। তুমি প্রতিটি স্টেট পরিবর্তন সঠিকভাবে ট্র্যাক করতে পারো এবং যেকোনো সময় পূর্ববর্তী এবং বর্তমান স্টেট দেখতে পারো।

উপসংহার

সারসংক্ষেপে, React-এ কাজ করার সময়, তোমার স্টেটকে অম্যুত হিসাবে দেখার জন্য খুবই গুরুত্বপূর্ণ। তুমি প্রাথমিক মান থেকে শুরু করে জটিল অবজেক্ট পর্যন্ত যাই ব্যবহার করো না কেন, সর্বদা পরিবর্তনের সময় নতুন ইনস্ট্যান্স তৈরি করো। এই অনুশীলনটি তোমাকে আরো নির্ভরযোগ্য, রক্ষণাবেক্ষণযোগ্য এবং কার্যকর কোড লেখার সাহায্য করবে।

তাহলে মনে রেখো: যখনই তোমার স্টেট আপডেট করার প্রয়োজন হয়, নিশ্চিত হও যে তুমি বিদ্যমান কিছু পরিবর্তনের পরিবর্তে একটি নতুন অবজেক্ট বা মান তৈরি করছ। এটি প্রথমদিকে কিছু অতিরিক্ত কাজ মনে হতে পারে, কিন্তু দীর্ঘমেয়াদে এর সুবিধাগুলি নিশ্চিতভাবে মূল্যবান হবে!

On this page