32 KiB
ব্যাংকিং অ্যাপ তৈরি করুন পার্ট ৪: স্টেট ম্যানেজমেন্টের ধারণা
প্রাক-লেকচার কুইজ
ভূমিকা
যখন একটি ওয়েব অ্যাপ্লিকেশন বড় হয়, তখন সমস্ত ডেটা প্রবাহ ট্র্যাক করা একটি চ্যালেঞ্জ হয়ে দাঁড়ায়। কোন কোড ডেটা পায়, কোন পৃষ্ঠা এটি ব্যবহার করে, কোথায় এবং কখন এটি আপডেট করতে হবে...এটি সহজেই জটিল কোডে পরিণত হতে পারে যা রক্ষণাবেক্ষণ করা কঠিন। এটি বিশেষভাবে সত্য যখন আপনার অ্যাপের বিভিন্ন পৃষ্ঠার মধ্যে ডেটা শেয়ার করতে হয়, যেমন ব্যবহারকারীর তথ্য। স্টেট ম্যানেজমেন্ট ধারণাটি সব ধরনের প্রোগ্রামে সবসময়ই বিদ্যমান ছিল, কিন্তু ওয়েব অ্যাপগুলো ক্রমাগত জটিল হয়ে উঠার সাথে সাথে এটি এখন উন্নয়নের সময় একটি গুরুত্বপূর্ণ বিষয় হয়ে উঠেছে।
এই চূড়ান্ত অংশে, আমরা আমাদের তৈরি করা অ্যাপটি পুনর্বিবেচনা করব কিভাবে স্টেট পরিচালিত হয়, ব্রাউজার রিফ্রেশের জন্য যেকোনো সময় সমর্থন প্রদান করে এবং ব্যবহারকারীর সেশনের মধ্যে ডেটা সংরক্ষণ করে।
পূর্বশর্ত
এই পাঠের জন্য আপনাকে ওয়েব অ্যাপের ডেটা ফেচিং অংশ সম্পন্ন করতে হবে। আপনাকে 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 কুকিজ ব্যবহার করতে পারেন যদি ডেটা সার্ভারের সাথে শেয়ার করতে হয়, যেমন 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()
-এ, 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 আপডেট করার যত্ন নেয়। এটি হলো যা আমরা ড্যাশবোর্ড রুট লোড হওয়ার সময় কল করতে চাই। রুট সংজ্ঞা আপডেট করুন:
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: refresh }
};
এখন ড্যাশবোর্ড রিফ্রেশ করার চেষ্টা করুন, এটি আপডেট করা অ্যাকাউন্ট ডেটা প্রদর্শন করা উচিত।
🚀 চ্যালেঞ্জ
এখন আমরা ড্যাশবোর্ড লোড হওয়ার প্রতিবার অ্যাকাউন্ট ডেটা পুনরায় লোড করি, আপনি কি মনে করেন আমরা এখনও সমস্ত অ্যাকাউন্ট ডেটা সংরক্ষণ করতে হবে?
একসাথে কাজ করার চেষ্টা করুন localStorage
থেকে কী সংরক্ষণ এবং লোড করা হয় তা পরিবর্তন করতে যাতে অ্যাপটি কাজ করার জন্য শুধুমাত্র যা একেবারে প্রয়োজন তা অন্তর্ভুক্ত করা হয়।
পোস্ট-লেকচার কুইজ
[পোস্ট-লেকচার কুইজ](https://ff-quizzes.netlify.app/web লেনদেন যোগ করার ডায়ালগ বাস্তবায়ন করুন
এখানে একটি উদাহরণ ফলাফল রয়েছে যা অ্যাসাইনমেন্ট সম্পন্ন করার পরে দেখা যাবে:
অস্বীকৃতি:
এই নথিটি AI অনুবাদ পরিষেবা Co-op Translator ব্যবহার করে অনুবাদ করা হয়েছে। আমরা যথাসাধ্য সঠিকতা নিশ্চিত করার চেষ্টা করি, তবে অনুগ্রহ করে মনে রাখবেন যে স্বয়ংক্রিয় অনুবাদে ত্রুটি বা অসঙ্গতি থাকতে পারে। মূল ভাষায় থাকা নথিটিকে প্রামাণিক উৎস হিসেবে বিবেচনা করা উচিত। গুরুত্বপূর্ণ তথ্যের জন্য, পেশাদার মানব অনুবাদ সুপারিশ করা হয়। এই অনুবাদ ব্যবহারের ফলে কোনো ভুল বোঝাবুঝি বা ভুল ব্যাখ্যা হলে আমরা দায়বদ্ধ থাকব না।