|
|
2 months ago | |
|---|---|---|
| .. | ||
| README.md | 2 months ago | |
| assignment.md | 3 months ago | |
README.md
একটি ব্যাংকিং অ্যাপ তৈরি করুন পর্ব ৪: স্টেট ম্যানেজমেন্টের ধারণা
প্রাক-লেকচার কুইজ
ভূমিকা
যখন একটি ওয়েব অ্যাপ্লিকেশন বড় হয়, তখন সমস্ত ডেটা প্রবাহ ট্র্যাক রাখা একটি চ্যালেঞ্জ হয়ে দাঁড়ায়। কোন কোড ডেটা পায়, কোন পৃষ্ঠা এটি ব্যবহার করে, কোথায় এবং কখন এটি আপডেট করতে হবে... সহজেই এমন কোড তৈরি হতে পারে যা বজায় রাখা কঠিন। এটি বিশেষত সত্য যখন আপনার অ্যাপের বিভিন্ন পৃষ্ঠার মধ্যে ডেটা শেয়ার করতে হয়, যেমন ব্যবহারকারীর ডেটা। স্টেট ম্যানেজমেন্ট ধারণাটি সব ধরণের প্রোগ্রামে সবসময়ই বিদ্যমান ছিল, তবে ওয়েব অ্যাপ্লিকেশনগুলির জটিলতা বাড়ার সাথে সাথে এটি এখন উন্নয়নের সময় একটি গুরুত্বপূর্ণ বিষয় হয়ে উঠেছে।
এই চূড়ান্ত অংশে, আমরা আমাদের তৈরি করা অ্যাপটি পুনর্বিবেচনা করব কিভাবে স্টেট পরিচালিত হয়, ব্রাউজার রিফ্রেশের জন্য যে কোনো সময় সমর্থন প্রদান করে এবং ব্যবহারকারীর সেশনের মধ্যে ডেটা সংরক্ষণ করে।
পূর্বশর্ত
এই পাঠের জন্য আপনাকে ডেটা ফেচিং অংশটি সম্পন্ন করতে হবে। আপনাকে Node.js ইনস্টল করতে হবে এবং সার্ভার API চালাতে হবে যাতে আপনি অ্যাকাউন্ট ডেটা পরিচালনা করতে পারেন।
আপনি টার্মিনালে এই কমান্ডটি চালিয়ে পরীক্ষা করতে পারেন যে সার্ভারটি সঠিকভাবে চলছে কিনা:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
স্টেট ম্যানেজমেন্ট পুনর্বিবেচনা
পূর্ববর্তী পাঠে, আমরা আমাদের অ্যাপে একটি মৌলিক স্টেট ধারণা প্রবর্তন করেছি account গ্লোবাল ভেরিয়েবলের মাধ্যমে, যা বর্তমানে লগ ইন করা ব্যবহারকারীর ব্যাংক ডেটা ধারণ করে। তবে, আমাদের বর্তমান বাস্তবায়নে কিছু ত্রুটি রয়েছে। ড্যাশবোর্ডে থাকাকালীন পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন। কী ঘটে?
বর্তমান কোডে ৩টি সমস্যা রয়েছে:
- স্টেট সংরক্ষিত হয় না, কারণ ব্রাউজার রিফ্রেশ আপনাকে লগইন পৃষ্ঠায় ফিরিয়ে নিয়ে যায়।
- স্টেট পরিবর্তন করে এমন একাধিক ফাংশন রয়েছে। অ্যাপটি বড় হলে এটি পরিবর্তনগুলি ট্র্যাক করা কঠিন করে তোলে এবং একটি আপডেট ভুলে যাওয়া সহজ।
- স্টেট পরিষ্কার করা হয় না, তাই যখন আপনি লগআউট এ ক্লিক করেন, তখনও অ্যাকাউন্ট ডেটা থেকে যায় যদিও আপনি লগইন পৃষ্ঠায় আছেন।
আমরা এই সমস্যাগুলি একে একে সমাধান করতে আমাদের কোড আপডেট করতে পারি, তবে এটি আরও কোড ডুপ্লিকেশন তৈরি করবে এবং অ্যাপটিকে আরও জটিল এবং বজায় রাখা কঠিন করে তুলবে। অথবা আমরা কয়েক মিনিটের জন্য বিরতি নিয়ে আমাদের কৌশল পুনর্বিবেচনা করতে পারি।
এখানে আমরা আসলে কোন সমস্যাগুলি সমাধান করার চেষ্টা করছি?
স্টেট ম্যানেজমেন্ট হল এই দুটি নির্দিষ্ট সমস্যার জন্য একটি ভাল পদ্ধতি খুঁজে বের করা:
- কীভাবে একটি অ্যাপে ডেটা প্রবাহগুলো বোধগম্য রাখা যায়?
- কীভাবে স্টেট ডেটা সর্বদা ব্যবহারকারীর ইন্টারফেসের সাথে সিঙ্কে রাখা যায় (এবং এর বিপরীত)?
আপনি যখন এগুলির যত্ন নেবেন, তখন আপনার যে কোনো অন্যান্য সমস্যা হয়তো ইতিমধ্যেই সমাধান হয়ে যাবে বা সমাধান করা সহজ হয়ে যাবে। এই সমস্যাগুলি সমাধানের জন্য অনেক সম্ভাব্য পদ্ধতি রয়েছে, তবে আমরা একটি সাধারণ সমাধান বেছে নেব যা ডেটা এবং এটি পরিবর্তনের উপায়গুলিকে কেন্দ্রীভূত করার ধারণার উপর ভিত্তি করে। ডেটা প্রবাহগুলো নিম্নরূপ হবে:
এখানে আমরা সেই অংশটি কভার করব না যেখানে ডেটা স্বয়ংক্রিয়ভাবে ভিউ আপডেটকে ট্রিগার করে, কারণ এটি 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 ইনিশিয়ালাইজেশন আপডেট করব:
let state = Object.freeze({
account: null
});
এরপর, register ফাংশন আপডেট করুন এবং state.account = result; অ্যাসাইনমেন্টটি প্রতিস্থাপন করুন:
updateState('account', result);
login ফাংশনের ক্ষেত্রেও একই কাজ করুন, state.account = data; প্রতিস্থাপন করুন:
updateState('account', data);
এখন আমরা লগআউট বাটনে ক্লিক করার সময় অ্যাকাউন্ট ডেটা পরিষ্কার না হওয়ার সমস্যাটি সমাধান করব।
একটি নতুন 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 ব্যবহার করে ক্লায়েন্টে একটি ডাটাবেস তৈরি করাও সম্ভব। এটি উন্নত ব্যবহারের ক্ষেত্রে বা যদি আপনাকে উল্লেখযোগ্য পরিমাণ ডেটা সংরক্ষণ করতে হয় তবে এটি আরও জটিল ব্যবহারের জন্য সংরক্ষিত।
কাজ
আমরা চাই আমাদের ব্যবহারকারীরা লগআউট বাটনে স্পষ্টভাবে ক্লিক না করা পর্যন্ত লগ ইন থাকুক, তাই আমরা localStorage ব্যবহার করে অ্যাকাউন্ট ডেটা সংরক্ষণ করব। প্রথমে, আমরা একটি কী সংজ্ঞায়িত করব যা আমরা আমাদের ডেটা সংরক্ষণ করতে ব্যবহার করব।
const storageKey = 'savedAccount';
তারপর updateState() ফাংশনের শেষে এই লাইনটি যোগ করুন:
localStorage.setItem(storageKey, JSON.stringify(state.account));
এটির মাধ্যমে, ব্যবহারকারীর অ্যাকাউন্ট ডেটা সংরক্ষিত থাকবে এবং পূর্বে কেন্দ্রীভূত করা সমস্ত স্টেট আপডেটের মতো সর্বদা আপডেট থাকবে। এটি সেই জায়গা যেখানে আমরা আমাদের পূর্ববর্তী রিফ্যাক্টরগুলির সমস্ত সুবিধা পেতে শুরু করি 🙂।
ডেটা সংরক্ষণ করা হয়েছে, তাই আমাদের অ্যাপটি লোড হওয়ার সময় এটি পুনরুদ্ধার করার যত্নও নিতে হবে। যেহেতু আমরা আরও ইনিশিয়ালাইজেশন কোড পেতে শুরু করব, এটি একটি নতুন 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();
এখানে আমরা সংরক্ষিত ডেটা পুনরুদ্ধার করি, এবং যদি কোনো ডেটা থাকে তবে আমরা স্টেটটি অনুযায়ী আপডেট করি। এটি রুট আপডেট করার আগে করা গুরুত্বপূর্ণ, কারণ পৃষ্ঠা আপডেটের সময় স্টেটের উপর নির্ভরশীল কোড থাকতে পারে।
আমরা ড্যাশবোর্ড পৃষ্ঠাটিকে আমাদের অ্যাপ্লিকেশনের ডিফল্ট পৃষ্ঠা বানাতে পারি, কারণ আমরা এখন অ্যাকাউন্ট ডেটা সংরক্ষণ করছি। যদি কোনো ডেটা পাওয়া না যায়, তবে ড্যাশবোর্ডটি যেকোনোভাবে লগইন পৃষ্ঠায় রিডিরেক্ট করার যত্ন নেয়। updateRoute()-এ, ফallback return navigate('/login'); প্রতিস্থাপন করুন 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 আপডেট করার যত্ন নেয়। এটি আমরা ড্যাশবোর্ড রুট লোড হওয়ার সময় কল করতে চাই। রুট সংজ্ঞাটি আপডেট করুন:
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: refresh }
};
এখন ড্যাশবোর্ড রিফ্রেশ করার চেষ্টা করুন, এটি আপডেট হওয়া অ্যাকাউন্ট ডেটা প্রদর্শন করা উচিত।
🚀 চ্যালেঞ্জ
এখন যেহেতু আমরা ড্যাশবোর্ড লোড হওয়ার প্রতিবার অ্যাকাউন্ট ডেটা পুনরায় লোড করি, আপনি কি মনে করেন যে আমরা এখনও সমস্ত অ্যাকাউন্ট ডেটা সংরক্ষণ করতে হবে?
একসঙ্গে কাজ করে localStorage থেকে কী সংরক্ষণ এবং লোড করা হয় তা পরিবর্তন করার চেষ্টা করুন যাতে অ্যাপটি কাজ করার জন্য একেবারে প্রয়োজনীয় জিনিসগুলোই অন্তর্ভুক্ত থাকে।
পোস্ট-লেকচার কুইজ
অ্যাসাইনমেন্ট
"লেনদেন যোগ করুন" ডায়ালগ বাস্তবায়ন করুন
অ্যাসাইনমেন্ট সম্পন্ন করার পর একটি উদাহরণ ফলাফল এখানে দেওয়া হলো:
অস্বীকৃতি:
এই নথিটি AI অনুবাদ পরিষেবা Co-op Translator ব্যবহার করে অনুবাদ করা হয়েছে। আমরা যথাসাধ্য সঠিকতার জন্য চেষ্টা করি, তবে অনুগ্রহ করে মনে রাখবেন যে স্বয়ংক্রিয় অনুবাদে ত্রুটি বা অসঙ্গতি থাকতে পারে। মূল ভাষায় থাকা নথিটিকে প্রামাণিক উৎস হিসেবে বিবেচনা করা উচিত। গুরুত্বপূর্ণ তথ্যের জন্য, পেশাদার মানব অনুবাদ সুপারিশ করা হয়। এই অনুবাদ ব্যবহারের ফলে কোনো ভুল বোঝাবুঝি বা ভুল ব্যাখ্যা হলে আমরা দায়বদ্ধ থাকব না।

