You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/bn/7-bank-project/4-state-management/README.md

32 KiB

ব্যাংকিং অ্যাপ তৈরি করুন পার্ট : স্টেট ম্যানেজমেন্টের ধারণা

প্রাক-লেকচার কুইজ

প্রাক-লেকচার কুইজ

ভূমিকা

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

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

পূর্বশর্ত

এই পাঠের জন্য আপনাকে ওয়েব অ্যাপের ডেটা ফেচিং অংশ সম্পন্ন করতে হবে। আপনাকে Node.js ইনস্টল করতে হবে এবং সার্ভার API চালাতে হবে স্থানীয়ভাবে যাতে আপনি অ্যাকাউন্ট ডেটা পরিচালনা করতে পারেন।

আপনি টার্মিনালে এই কমান্ডটি চালিয়ে পরীক্ষা করতে পারেন যে সার্ভারটি সঠিকভাবে চলছে কিনা:

curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result

স্টেট ম্যানেজমেন্ট পুনর্বিবেচনা করুন

পূর্ববর্তী পাঠে, আমরা আমাদের অ্যাপে একটি মৌলিক স্টেট ধারণা প্রবর্তন করেছি account নামক একটি গ্লোবাল ভেরিয়েবল দিয়ে, যা বর্তমানে লগ ইন করা ব্যবহারকারীর ব্যাংক ডেটা ধারণ করে। তবে, আমাদের বর্তমান বাস্তবায়নে কিছু ত্রুটি রয়েছে। ড্যাশবোর্ডে থাকাকালীন পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন। কী ঘটে?

বর্তমান কোডে তিনটি সমস্যা রয়েছে:

  • স্টেট সংরক্ষিত নয়, কারণ ব্রাউজার রিফ্রেশ আপনাকে লগইন পৃষ্ঠায় ফিরিয়ে নিয়ে যায়।
  • স্টেট পরিবর্তন করার জন্য একাধিক ফাংশন রয়েছে। অ্যাপটি বড় হলে এটি পরিবর্তনগুলো ট্র্যাক করা কঠিন করে তুলতে পারে এবং একটি আপডেট করতে ভুলে যাওয়া সহজ।
  • স্টেট পরিষ্কার করা হয় না, তাই যখন আপনি লগআউট ক্লিক করেন তখন অ্যাকাউন্ট ডেটা এখনও সেখানে থাকে যদিও আপনি লগইন পৃষ্ঠায় রয়েছেন।

আমরা একে একে এই সমস্যাগুলো মোকাবেলা করার জন্য আমাদের কোড আপডেট করতে পারি, তবে এটি আরও কোড ডুপ্লিকেশন তৈরি করবে এবং অ্যাপটিকে আরও জটিল এবং রক্ষণাবেক্ষণ করা কঠিন করে তুলবে। অথবা আমরা কয়েক মিনিটের জন্য বিরতি নিতে পারি এবং আমাদের কৌশল পুনর্বিবেচনা করতে পারি।

আমরা এখানে আসলে কোন সমস্যাগুলো সমাধান করার চেষ্টা করছি?

স্টেট ম্যানেজমেন্ট হলো এই দুটি নির্দিষ্ট সমস্যার সমাধানের জন্য একটি ভালো পদ্ধতি খুঁজে বের করা:

  • কীভাবে একটি অ্যাপের ডেটা প্রবাহগুলোকে বোধগম্য রাখা যায়?
  • কীভাবে স্টেট ডেটাকে সর্বদা ব্যবহারকারীর ইন্টারফেসের সাথে (এবং এর বিপরীতে) সিঙ্কে রাখা যায়?

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

HTML, ব্যবহারকারীর ক্রিয়া এবং স্টেটের মধ্যে ডেটা প্রবাহ দেখানো স্কিমা

আমরা এখানে সেই অংশটি কভার করব না যেখানে ডেটা স্বয়ংক্রিয়ভাবে ভিউ আপডেটকে ট্রিগার করে, কারণ এটি Reactive Programming-এর আরও উন্নত ধারণার সাথে সম্পর্কিত। এটি একটি গভীর ডাইভের জন্য একটি ভালো অনুসরণ বিষয়।

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

কাজ

আমরা কিছু রিফ্যাক্টরিং দিয়ে শুরু করব। account ঘোষণাটি প্রতিস্থাপন করুন:

let account = null;

এর সাথে:

let state = {
  account: null
};

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

আমাদের এটি ব্যবহারকারী ফাংশনগুলোও আপডেট করতে হবে। register() এবং login() ফাংশনে, account = ... প্রতিস্থাপন করুন state.account = ...;

updateDashboard() ফাংশনের শুরুতে এই লাইনটি যোগ করুন:

const account = state.account;

এই রিফ্যাক্টরিং নিজেই খুব বেশি উন্নতি আনেনি, তবে ধারণাটি ছিল পরবর্তী পরিবর্তনের জন্য ভিত্তি স্থাপন করা।

ডেটা পরিবর্তন ট্র্যাক করুন

এখন আমরা আমাদের ডেটা সংরক্ষণের জন্য state অবজেক্টটি স্থাপন করেছি, পরবর্তী ধাপ হলো আপডেটগুলো কেন্দ্রীভূত করা। লক্ষ্য হলো যে কোনো পরিবর্তন এবং কখন তা ঘটে তা ট্র্যাক করা সহজ করা।

state অবজেক্টে পরিবর্তন করা এড়াতে, এটিকে immutable বিবেচনা করাও একটি ভালো অভ্যাস, যার অর্থ এটি মোটেও পরিবর্তন করা যাবে না। এর অর্থ হলো আপনি যদি এতে কিছু পরিবর্তন করতে চান তবে আপনাকে একটি নতুন স্টেট অবজেক্ট তৈরি করতে হবে। এটি করে, আপনি সম্ভাব্য অনাকাঙ্ক্ষিত side effects সম্পর্কে একটি সুরক্ষা তৈরি করেন এবং আপনার অ্যাপে নতুন বৈশিষ্ট্য বাস্তবায়নের জন্য সম্ভাবনা খুলে দেন যেমন undo/redo বাস্তবায়ন করা, পাশাপাশি এটি ডিবাগ করা সহজ করে তোলে। উদাহরণস্বরূপ, আপনি স্টেটে করা প্রতিটি পরিবর্তন লগ করতে পারেন এবং একটি বাগের উৎস বুঝতে পরিবর্তনের একটি ইতিহাস রাখতে পারেন।

জাভাস্ক্রিপ্টে, আপনি Object.freeze() ব্যবহার করতে পারেন একটি অবজেক্টের অপরিবর্তনীয় সংস্করণ তৈরি করতে। আপনি যদি একটি অপরিবর্তনীয় অবজেক্টে পরিবর্তন করার চেষ্টা করেন, তবে একটি ব্যতিক্রম উত্থাপিত হবে।

আপনি কি জানেন একটি shallow এবং একটি deep অপরিবর্তনীয় অবজেক্টের মধ্যে পার্থক্য কী? আপনি এটি সম্পর্কে এখানে পড়তে পারেন।

কাজ

একটি নতুন updateState() ফাংশন তৈরি করুন:

function updateState(property, newData) {
  state = Object.freeze({
    ...state,
    [property]: newData
  });
}

এই ফাংশনে, আমরা একটি নতুন স্টেট অবজেক্ট তৈরি করছি এবং পূর্ববর্তী স্টেট থেকে ডেটা কপি করছি spread (...) operator ব্যবহার করে। তারপর আমরা একটি নির্দিষ্ট প্রপার্টি স্টেট অবজেক্টে নতুন ডেটা দিয়ে ওভাররাইড করি bracket notation [property] ব্যবহার করে। অবশেষে, আমরা Object.freeze() ব্যবহার করে অবজেক্টটি লক করি যাতে পরিবর্তন প্রতিরোধ করা যায়। বর্তমানে স্টেটে শুধুমাত্র account প্রপার্টি সংরক্ষিত আছে, তবে এই পদ্ধতির মাধ্যমে আপনি স্টেটে যত প্রপার্টি প্রয়োজন যোগ করতে পারেন।

আমরা নিশ্চিত করতে state initialization আপডেট করব যাতে প্রাথমিক স্টেটও ফ্রোজেন থাকে:

let state = Object.freeze({
  account: null
});

এর পরে, register ফাংশন আপডেট করুন state.account = result; অ্যাসাইনমেন্ট প্রতিস্থাপন করে:

updateState('account', result);

login ফাংশনের ক্ষেত্রেও একই কাজ করুন, state.account = data; প্রতিস্থাপন করে:

updateState('account', data);

আমরা এখন Logout ক্লিক করার সময় অ্যাকাউন্ট ডেটা পরিষ্কার না হওয়ার সমস্যাটি সমাধান করার সুযোগ নেব।

একটি নতুন logout() ফাংশন তৈরি করুন:

function logout() {
  updateState('account', null);
  navigate('/login');
}

updateDashboard()-এ, return navigate('/login'); রিডিরেকশনটি প্রতিস্থাপন করুন return logout(); দিয়ে;

একটি নতুন অ্যাকাউন্ট নিবন্ধন করুন, লগআউট করুন এবং আবার লগইন করুন এটি এখনও সঠিকভাবে কাজ করছে কিনা তা পরীক্ষা করতে।

টিপ: আপনি ব্রাউজারের ডেভেলপমেন্ট টুলসের কনসোলে updateState()-এর নিচে console.log(state) যোগ করে সমস্ত স্টেট পরিবর্তন দেখতে পারেন।

স্টেট সংরক্ষণ করুন

বেশিরভাগ ওয়েব অ্যাপ সঠিকভাবে কাজ করতে ডেটা সংরক্ষণ করতে হয়। সমস্ত গুরুত্বপূর্ণ ডেটা সাধারণত একটি ডাটাবেসে সংরক্ষণ করা হয় এবং একটি সার্ভার API এর মাধ্যমে অ্যাক্সেস করা হয়, যেমন আমাদের ক্ষেত্রে ব্যবহারকারীর অ্যাকাউন্ট ডেটা। তবে কখনও কখনও, ব্রাউজারে চলমান ক্লায়েন্ট অ্যাপে কিছু ডেটা সংরক্ষণ করাও আকর্ষণীয় হতে পারে, একটি ভালো ব্যবহারকারীর অভিজ্ঞতার জন্য বা লোডিং পারফরম্যান্স উন্নত করতে।

যখন আপনি আপনার ব্রাউজারে ডেটা সংরক্ষণ করতে চান, তখন কয়েকটি গুরুত্বপূর্ণ প্রশ্ন জিজ্ঞাসা করা উচিত:

  • ডেটা কি সংবেদনশীল? আপনি ক্লায়েন্টে কোনো সংবেদনশীল ডেটা সংরক্ষণ এড়ানো উচিত, যেমন ব্যবহারকারীর পাসওয়ার্ড।
  • আপনার এই ডেটা কতক্ষণ ধরে রাখতে হবে? আপনি কি এই ডেটা শুধুমাত্র বর্তমান সেশনের জন্য অ্যাক্সেস করার পরিকল্পনা করছেন নাকি আপনি এটি চিরতরে সংরক্ষণ করতে চান?

ওয়েব অ্যাপের ভিতরে তথ্য সংরক্ষণের জন্য বিভিন্ন উপায় রয়েছে, আপনি যা অর্জন করতে চান তার উপর নির্ভর করে। উদাহরণস্বরূপ, আপনি একটি সার্চ কোয়েরি সংরক্ষণ করতে URL ব্যবহার করতে পারেন এবং এটি ব্যবহারকারীদের মধ্যে শেয়ারযোগ্য করতে পারেন। আপনি HTTP cookies ব্যবহার করতে পারেন যদি ডেটা সার্ভারের সাথে শেয়ার করতে হয়, যেমন authentication তথ্য।

আরেকটি বিকল্প হলো ডেটা সংরক্ষণের জন্য ব্রাউজারের অনেক API-এর মধ্যে একটি ব্যবহার করা। দুটি বিশেষভাবে আকর্ষণীয়:

  • localStorage: একটি Key/Value store যা বিভিন্ন সেশনের মধ্যে নির্দিষ্ট ওয়েব সাইটের ডেটা সংরক্ষণ করতে দেয়। এতে সংরক্ষিত ডেটা কখনোই মেয়াদোত্তীর্ণ হয় না।
  • sessionStorage: এটি localStorage এর মতোই কাজ করে তবে এতে সংরক্ষিত ডেটা সেশনের শেষে (যখন ব্রাউজার বন্ধ হয়) মুছে ফেলা হয়।

উল্লেখ্য যে এই দুটি API শুধুমাত্র strings সংরক্ষণ করতে দেয়। আপনি যদি জটিল অবজেক্ট সংরক্ষণ করতে চান, তবে আপনাকে এটি JSON ফরম্যাটে সিরিয়ালাইজ করতে হবে JSON.stringify() ব্যবহার করে।

আপনি যদি এমন একটি ওয়েব অ্যাপ তৈরি করতে চান যা সার্ভার ছাড়াই কাজ করে, তবে IndexedDB API ব্যবহার করে ক্লায়েন্টে একটি ডাটাবেস তৈরি করাও সম্ভব। এটি উন্নত ব্যবহারের ক্ষেত্রে বা যদি উল্লেখযোগ্য পরিমাণ ডেটা সংরক্ষণ করতে হয়, তবে এটি ব্যবহার করা আরও জটিল।

কাজ

আমরা চাই আমাদের ব্যবহারকারীরা Logout বোতামে স্পষ্টভাবে ক্লিক না করা পর্যন্ত লগ ইন অবস্থায় থাকুক, তাই আমরা localStorage ব্যবহার করব অ্যাকাউন্ট ডেটা সংরক্ষণ করতে। প্রথমে, আসুন একটি key সংজ্ঞায়িত করি যা আমরা আমাদের ডেটা সংরক্ষণের জন্য ব্যবহার করব।

const storageKey = 'savedAccount';

তারপর updateState() ফাংশনের শেষে এই লাইনটি যোগ করুন:

localStorage.setItem(storageKey, JSON.stringify(state.account));

এর মাধ্যমে, ব্যবহারকারীর অ্যাকাউন্ট ডেটা সংরক্ষিত থাকবে এবং আমরা পূর্বে কেন্দ্রীভূত করা সমস্ত স্টেট আপডেটের জন্য সর্বদা আপ-টু-ডেট থাকবে। এটি হলো যেখানে আমরা আমাদের পূর্ববর্তী রিফ্যাক্টরগুলো থেকে উপকৃত হতে শুরু করি 🙂

ডেটা সংরক্ষিত হওয়ার কারণে, অ্যাপটি লোড হওয়ার সময় এটি পুনরুদ্ধার করাও আমাদের যত্ন নিতে হবে। যেহেতু আমরা আরও initialization কোড শুরু করতে যাচ্ছি, এটি একটি নতুন init ফাংশন তৈরি করার জন্য একটি ভালো ধারণা হতে পারে, যা app.js-এর নিচে আমাদের পূর্ববর্তী কোডও অন্তর্ভুক্ত করে:

function init() {
  const savedAccount = localStorage.getItem(storageKey);
  if (savedAccount) {
    updateState('account', JSON.parse(savedAccount));
  }

  // Our previous initialization code
  window.onpopstate = () => updateRoute();
  updateRoute();
}

init();

এখানে আমরা সংরক্ষিত ডেটা পুনরুদ্ধার করি, এবং যদি কোনো ডেটা থাকে তবে আমরা স্টেটটি অনুযায়ী আপডেট করি। এটি route আপডেট করার আগে করা গুরুত্বপূর্ণ, কারণ পৃষ্ঠা আপডেটের সময় স্টেটের উপর নির্ভরশীল কোড থাকতে পারে।

আমরা আমাদের অ্যাপ্লিকেশনের ডিফল্ট পৃষ্ঠা Dashboard পৃষ্ঠা করতে পারি, কারণ আমরা এখন অ্যাকাউন্ট ডেটা সংরক্ষণ করছি। যদি কোনো ডেটা না পাওয়া যায়, তাহলে ড্যাশবোর্ড যেকোনোভাবে Login পৃষ্ঠায় রিডিরেক্ট করার যত্ন নেয়। updateRoute()-এ, return navigate('/login'); ফallback প্রতিস্থাপন করুন return navigate('/dashboard'); দিয়ে।

এখন অ্যাপে লগইন করুন এবং পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন। আপনি ড্যাশবোর্ডে থাকা উচিত। এই আপডেটের মাধ্যমে আমরা আমাদের সমস্ত প্রাথমিক সমস্যার যত্ন নিয়েছি...

ডেটা রিফ্রেশ করুন

...কিন্তু আমরা হয়তো একটি নতুন সমস্যা তৈরি করেছি। ওহ!

test অ্যাকাউন্ট ব্যবহার করে ড্যাশবোর্ডে যান, তারপর একটি নতুন লেনদেন তৈরি করতে একটি টার্মিনালে এই কমান্ডটি চালান:

curl --request POST \
     --header "Content-Type: application/json" \
     --data "{ \"date\": \"2020-07-24\", \"object\": \"Bought book\", \"amount\": -20 }" \
     http://localhost:5000/api/accounts/test/transactions

এখন ব্রাউজারে ড্যাশবোর্ড পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন। কী ঘটে? আপনি কি নতুন লেনদেনটি দেখতে পাচ্ছেন?

স্টেটটি localStorage এর মাধ্যমে অনির্দিষ্টকালের জন্য সংরক্ষিত থাকে, তবে এর অর্থ হলো এটি কখনোই আপডেট হয় না যতক্ষণ না আপনি অ্যাপ থেকে লগ আউট করেন এবং আবার লগ ইন করেন!

এটি ঠিক করার একটি সম্ভাব্য কৌশল হলো ড্যাশবোর্ড লোড হওয়ার সময় প্রতিবার অ্যাকাউন্ট ডেটা পুনরায় লোড করা, স্থবির ডেটা এড়াতে।

কাজ

একটি নতুন updateAccountData ফাংশন তৈরি করুন:

async function updateAccountData() {
  const account = state.account;
  if (!account) {
    return logout();
  }

  const data = await getAccount(account.user);
  if (data.error) {
    return logout();
  }

  updateState('account', data);
}

এই পদ্ধতিটি পরীক্ষা করে যে আমরা বর্তমানে লগ ইন অবস্থায় আছি, তারপর সার্ভার থেকে অ্যাকাউন্ট ডেটা পুনরায় লোড করে।

refresh নামে আরেকটি ফাংশন তৈরি করুন:

async function refresh() {
  await updateAccountData();
  updateDashboard();
}

এটি অ্যাকাউন্ট ডেটা আপডেট করে, তারপর ড্যাশবোর্ড পৃষ্ঠার HTML আপডেট করার যত্ন নেয়। এটি হলো যা আমরা ড্যাশবোর্ড route লোড করার সময় কল করতে চাই। route সংজ্ঞা আপডেট করুন:

const routes = {
  '/login': { templateId: 'login' },
  '/dashboard': { templateId: 'dashboard', init: refresh }
};

এখন ড্যাশবোর্ড রিফ্রেশ করার চেষ্টা করুন, এটি আপডেট করা অ্যাকাউন্ট ডেটা প্রদর্শন করা উচিত।


🚀 চ্যালেঞ্জ

এখন আমরা প্রতিবার ড্যাশবোর্ড লোড করার সময় অ্যাকাউন্ট ডেটা পুনরায় লোড করি, আপনি কি মনে করেন আমরা এখনও সমস্ত অ্যাকাউন্ট ডেটা সংরক্ষণ করতে হবে?

একসাথে কাজ করে localStorage থেকে কী সংরক্ষণ করা এবং লোড করা উচিত তা পরিবর্তন করার চেষ্টা করুন যাতে অ্যাপটি কাজ করার জন্য শুধুমাত্র যা একেবারে প্রয়োজন তা অন্তর্ভুক্ত থাকে।

পোস্ট-লেকচার কুইজ

পোস্ট-লেকচার কুইজ

অ্যাসাইনমেন্ট

"Add transaction" ডায়ালগ বাস্তবায়ন করুন

অ্যাসাইনমেন্ট সম্পন্ন করার পর একটি উদাহরণ ফলাফল এখানে দেওয়া হলো:

"Add transaction" ডায়ালগের একটি উদাহরণ স্ক্রিনশট

অস্বীকৃতি:
এই নথিটি AI অনুবাদ পরিষেবা Co-op Translator ব্যবহার করে অনুবাদ করা হয়েছে। আমরা যথাসাধ্য সঠিকতা নিশ্চিত করার চেষ্টা করি, তবে অনুগ্রহ করে মনে রাখবেন যে স্বয়ংক্রিয় অনুবাদে ত্রুটি বা অসঙ্গতি থাকতে পারে। মূল ভাষায় থাকা নথিটিকে প্রামাণিক উৎস হিসেবে বিবেচনা করা উচিত। গুরুত্বপূর্ণ তথ্যের জন্য, পেশাদার মানব অনুবাদ সুপারিশ করা হয়। এই অনুবাদ ব্যবহারের ফলে কোনো ভুল বোঝাবুঝি বা ভুল ব্যাখ্যা হলে আমরা দায়বদ্ধ থাকব না।