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/ur/7-bank-project/4-state-management/README.md

24 KiB

بینکنگ ایپ بنائیں حصہ 4: اسٹیٹ مینجمنٹ کے تصورات

لیکچر سے پہلے کا کوئز

لیکچر سے پہلے کا کوئز

تعارف

جب ایک ویب ایپلیکیشن بڑھتی ہے، تو تمام ڈیٹا کے بہاؤ کو ٹریک کرنا ایک چیلنج بن جاتا ہے۔ کون سا کوڈ ڈیٹا حاصل کرتا ہے، کون سا صفحہ اسے استعمال کرتا ہے، کہاں اور کب اسے اپ ڈیٹ کرنے کی ضرورت ہے... یہ آسانی سے پیچیدہ کوڈ میں بدل سکتا ہے جسے برقرار رکھنا مشکل ہو۔ یہ خاص طور پر اس وقت سچ ہے جب آپ کو اپنی ایپ کے مختلف صفحات کے درمیان ڈیٹا شیئر کرنے کی ضرورت ہو، جیسے کہ صارف کا ڈیٹا۔ اسٹیٹ مینجمنٹ کا تصور ہمیشہ سے ہر قسم کے پروگراموں میں موجود رہا ہے، لیکن جیسے جیسے ویب ایپس کی پیچیدگی بڑھتی جا رہی ہے، یہ ترقی کے دوران ایک اہم نکتہ بن گیا ہے۔

اس آخری حصے میں، ہم اس ایپ پر نظر ڈالیں گے جو ہم نے بنائی ہے تاکہ اسٹیٹ کو منظم کرنے کے طریقے پر دوبارہ غور کیا جا سکے، براؤزر ریفریش کو کسی بھی وقت سپورٹ کرنے اور صارف کے سیشنز کے دوران ڈیٹا کو برقرار رکھنے کی اجازت دی جا سکے۔

پیشگی شرائط

آپ کو اس سبق کے لیے ویب ایپ کے ڈیٹا فچنگ والے حصے کو مکمل کرنا ہوگا۔ آپ کو Node.js انسٹال کرنے اور سرور API کو مقامی طور پر چلانے کی بھی ضرورت ہوگی تاکہ آپ اکاؤنٹ کے ڈیٹا کو منظم کر سکیں۔

آپ یہ کمانڈ ٹرمینل میں چلا کر یہ جانچ سکتے ہیں کہ سرور صحیح طریقے سے چل رہا ہے یا نہیں:

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

اسٹیٹ مینجمنٹ پر دوبارہ غور کریں

پچھلے سبق میں، ہم نے اپنی ایپ میں اسٹیٹ کے ایک بنیادی تصور کو متعارف کرایا تھا، جس میں عالمی account ویری ایبل شامل تھا جو موجودہ لاگ ان صارف کے بینک ڈیٹا کو رکھتا ہے۔ تاہم، ہمارے موجودہ نفاذ میں کچھ خامیاں ہیں۔ ڈیش بورڈ پر ہونے کے دوران صفحہ کو ریفریش کرنے کی کوشش کریں۔ کیا ہوتا ہے؟

موجودہ کوڈ میں 3 مسائل ہیں:

  • اسٹیٹ محفوظ نہیں ہے، کیونکہ براؤزر ریفریش آپ کو لاگ ان صفحے پر واپس لے جاتا ہے۔
  • اسٹیٹ کو تبدیل کرنے کے لیے متعدد فنکشنز موجود ہیں۔ جیسے جیسے ایپ بڑھتی ہے، تبدیلیوں کو ٹریک کرنا مشکل ہو سکتا ہے اور کسی ایک کو اپ ڈیٹ کرنا بھول جانا آسان ہو جاتا ہے۔
  • اسٹیٹ صاف نہیں کی جاتی، لہذا جب آپ Logout پر کلک کرتے ہیں تو اکاؤنٹ کا ڈیٹا اب بھی موجود ہوتا ہے حالانکہ آپ لاگ ان صفحے پر ہیں۔

ہم اپنے کوڈ کو ان مسائل کو ایک ایک کر کے حل کرنے کے لیے اپ ڈیٹ کر سکتے ہیں، لیکن اس سے کوڈ کی نقل میں اضافہ ہوگا اور ایپ کو مزید پیچیدہ اور برقرار رکھنے میں مشکل بنا دے گا۔ یا ہم چند منٹ کے لیے رک سکتے ہیں اور اپنی حکمت عملی پر دوبارہ غور کر سکتے ہیں۔

ہم یہاں اصل میں کون سے مسائل حل کرنے کی کوشش کر رہے ہیں؟

اسٹیٹ مینجمنٹ کا مقصد ان دو خاص مسائل کو حل کرنے کے لیے ایک اچھا طریقہ تلاش کرنا ہے:

  • ایپ میں ڈیٹا کے بہاؤ کو سمجھنے کے قابل کیسے رکھا جائے؟
  • اسٹیٹ ڈیٹا کو ہمیشہ صارف کے انٹرفیس کے ساتھ (اور اس کے برعکس) ہم آہنگ کیسے رکھا جائے؟

ایک بار جب آپ ان کا خیال رکھ لیں، تو آپ کے پاس موجود کوئی بھی دیگر مسائل یا تو پہلے ہی حل ہو چکے ہوں گے یا انہیں حل کرنا آسان ہو جائے گا۔ ان مسائل کو حل کرنے کے لیے بہت سے ممکنہ طریقے ہیں، لیکن ہم ایک عام حل کے ساتھ جائیں گے جو ڈیٹا اور اسے تبدیل کرنے کے طریقوں کو مرکزی بنانے پر مشتمل ہے۔ ڈیٹا کے بہاؤ اس طرح جائیں گے:

HTML، صارف کے اعمال اور اسٹیٹ کے درمیان ڈیٹا کے بہاؤ کو دکھانے والی اسکیمہ

ہم یہاں اس حصے کا احاطہ نہیں کریں گے جہاں ڈیٹا خود بخود ویو اپ ڈیٹ کو متحرک کرتا ہے، کیونکہ یہ ری ایکٹو پروگرامنگ کے مزید جدید تصورات سے جڑا ہوا ہے۔ اگر آپ گہرائی میں جانا چاہتے ہیں تو یہ ایک اچھا فالو اپ موضوع ہے۔

اسٹیٹ مینجمنٹ کے مختلف طریقوں کے ساتھ بہت سی لائبریریاں موجود ہیں، Redux ایک مقبول آپشن ہے۔ ان تصورات اور پیٹرنز پر ایک نظر ڈالیں کیونکہ یہ اکثر یہ سیکھنے کا ایک اچھا طریقہ ہوتا ہے کہ آپ کو بڑی ویب ایپس میں کن ممکنہ مسائل کا سامنا ہو سکتا ہے اور انہیں کیسے حل کیا جا سکتا ہے۔

کام

ہم تھوڑا سا ریفیکٹرنگ سے شروع کریں گے۔ account کی ڈیکلریشن کو تبدیل کریں:

let account = null;

اس کے ساتھ:

let state = {
  account: null
};

خیال یہ ہے کہ ہمارے ایپ کے تمام ڈیٹا کو ایک واحد اسٹیٹ آبجیکٹ میں مرکزی بنایا جائے۔ فی الحال ہمارے پاس اسٹیٹ میں صرف account ہے، لہذا اس سے زیادہ فرق نہیں پڑتا، لیکن یہ ارتقاء کے لیے ایک راستہ بناتا ہے۔

ہمیں ان فنکشنز کو بھی اپ ڈیٹ کرنا ہوگا جو اسے استعمال کرتے ہیں۔ register() اور login() فنکشنز میں، account = ... کو state.account = ... سے تبدیل کریں؛

updateDashboard() فنکشن کے آغاز میں، یہ لائن شامل کریں:

const account = state.account;

یہ ریفیکٹرنگ بذات خود زیادہ بہتری نہیں لائی، لیکن اس کا مقصد اگلی تبدیلیوں کے لیے بنیاد رکھنا تھا۔

ڈیٹا کی تبدیلیوں کو ٹریک کریں

اب جب کہ ہم نے اپنے ڈیٹا کو ذخیرہ کرنے کے لیے state آبجیکٹ کو جگہ دی ہے، اگلا قدم اپ ڈیٹس کو مرکزی بنانا ہے۔ مقصد یہ ہے کہ کسی بھی تبدیلی اور ان کے ہونے کے وقت کو ٹریک کرنا آسان بنایا جائے۔

state آبجیکٹ میں تبدیلیاں کرنے سے بچنے کے لیے، اسے ناقابل تغیر سمجھنا بھی ایک اچھا عمل ہے، یعنی اسے بالکل بھی تبدیل نہیں کیا جا سکتا۔ اس کا مطلب یہ بھی ہے کہ اگر آپ اس میں کچھ تبدیل کرنا چاہتے ہیں تو آپ کو ایک نیا اسٹیٹ آبجیکٹ بنانا ہوگا۔ ایسا کرنے سے، آپ ممکنہ ناپسندیدہ سائیڈ ایفیکٹس کے بارے میں تحفظ پیدا کرتے ہیں، اور اپنی ایپ میں نئی خصوصیات کے امکانات کھولتے ہیں جیسے کہ انڈو/ریڈو کو نافذ کرنا، جبکہ ڈیبگ کرنا بھی آسان بناتے ہیں۔ مثال کے طور پر، آپ اسٹیٹ میں کی گئی ہر تبدیلی کو لاگ کر سکتے ہیں اور کسی بگ کے ماخذ کو سمجھنے کے لیے تبدیلیوں کی تاریخ رکھ سکتے ہیں۔

جاوا اسکرپٹ میں، آپ Object.freeze() کا استعمال کر کے کسی آبجیکٹ کا ناقابل تغیر ورژن بنا سکتے ہیں۔ اگر آپ کسی ناقابل تغیر آبجیکٹ میں تبدیلیاں کرنے کی کوشش کرتے ہیں، تو ایک استثناء پیدا ہوگا۔

کیا آپ جانتے ہیں کہ شالو اور ڈیپ ناقابل تغیر آبجیکٹ میں کیا فرق ہے؟ آپ اس کے بارے میں یہاں پڑھ سکتے ہیں۔

کام

آئیے ایک نیا updateState() فنکشن بناتے ہیں:

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

اس فنکشن میں، ہم ایک نیا اسٹیٹ آبجیکٹ بنا رہے ہیں اور پچھلے اسٹیٹ سے ڈیٹا کو اسپریڈ (...) آپریٹر کا استعمال کرتے ہوئے کاپی کر رہے ہیں۔ پھر ہم اسٹیٹ آبجیکٹ کی ایک خاص پراپرٹی کو نئے ڈیٹا کے ساتھ بریکٹ نوٹیشن [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 پر کلک کرتا ہے تو اکاؤنٹ کا ڈیٹا صاف نہیں ہوتا۔

ایک نیا فنکشن logout() بنائیں:

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

updateDashboard() میں، ری ڈائریکشن return navigate('/login'); کو return logout(); سے تبدیل کریں؛

ایک نیا اکاؤنٹ رجسٹر کرنے، لاگ آؤٹ کرنے اور دوبارہ لاگ ان کرنے کی کوشش کریں تاکہ یہ چیک کیا جا سکے کہ سب کچھ اب بھی صحیح طریقے سے کام کر رہا ہے۔

ٹپ: آپ updateState() کے آخر میں console.log(state) شامل کر کے اور اپنے براؤزر کے ڈویلپمنٹ ٹولز میں کنسول کھول کر تمام اسٹیٹ تبدیلیوں پر نظر ڈال سکتے ہیں۔

اسٹیٹ کو برقرار رکھیں

زیادہ تر ویب ایپس کو صحیح طریقے سے کام کرنے کے لیے ڈیٹا کو برقرار رکھنے کی ضرورت ہوتی ہے۔ تمام اہم ڈیٹا عام طور پر ڈیٹا بیس میں محفوظ کیا جاتا ہے اور سرور API کے ذریعے اس تک رسائی حاصل کی جاتی ہے، جیسے کہ ہمارے معاملے میں صارف کے اکاؤنٹ کا ڈیٹا۔ لیکن بعض اوقات، بہتر صارف کے تجربے یا لوڈنگ کی کارکردگی کو بہتر بنانے کے لیے براؤزر میں چلنے والی کلائنٹ ایپ پر کچھ ڈیٹا کو برقرار رکھنا بھی دلچسپ ہوتا ہے۔

جب آپ اپنے براؤزر میں ڈیٹا کو برقرار رکھنا چاہتے ہیں، تو آپ کو اپنے آپ سے کچھ اہم سوالات پوچھنے چاہئیں:

  • کیا ڈیٹا حساس ہے؟ آپ کو کلائنٹ پر کسی بھی حساس ڈیٹا کو ذخیرہ کرنے سے گریز کرنا چاہیے، جیسے کہ صارف کے پاس ورڈز۔
  • آپ کو یہ ڈیٹا کتنی دیر تک رکھنا ہے؟ کیا آپ اس ڈیٹا تک صرف موجودہ سیشن کے لیے رسائی حاصل کرنے کا ارادہ رکھتے ہیں یا آپ چاہتے ہیں کہ یہ ہمیشہ کے لیے محفوظ رہے؟

ویب ایپ کے اندر معلومات کو ذخیرہ کرنے کے کئی طریقے ہیں، اس پر منحصر ہے کہ آپ کیا حاصل کرنا چاہتے ہیں۔ مثال کے طور پر، آپ تلاش کے استفسار کو ذخیرہ کرنے کے لیے URLs کا استعمال کر سکتے ہیں، اور اسے صارفین کے درمیان شیئر کرنے کے قابل بنا سکتے ہیں۔ اگر ڈیٹا کو سرور کے ساتھ شیئر کرنے کی ضرورت ہو، جیسے کہ تصدیق کی معلومات، تو آپ HTTP کوکیز کا استعمال بھی کر سکتے ہیں۔

ایک اور آپشن یہ ہے کہ ڈیٹا کو ذخیرہ کرنے کے لیے براؤزر APIs میں سے ایک کا استعمال کریں۔ ان میں سے دو خاص طور پر دلچسپ ہیں:

  • localStorage: ایک Key/Value اسٹور جو مختلف سیشنز کے دوران موجودہ ویب سائٹ کے لیے مخصوص ڈیٹا کو برقرار رکھنے کی اجازت دیتا ہے۔ اس میں محفوظ کردہ ڈیٹا کبھی ختم نہیں ہوتا۔
  • sessionStorage: یہ localStorage کی طرح کام کرتا ہے سوائے اس کے کہ اس میں محفوظ کردہ ڈیٹا سیشن ختم ہونے پر (جب براؤزر بند ہو جاتا ہے) صاف ہو جاتا ہے۔

نوٹ کریں کہ یہ دونوں APIs صرف اسٹرنگز کو ذخیرہ کرنے کی اجازت دیتے ہیں۔ اگر آپ پیچیدہ آبجیکٹس کو ذخیرہ کرنا چاہتے ہیں، تو آپ کو اسے JSON فارمیٹ میں سیریلائز کرنے کی ضرورت ہوگی، JSON.stringify() کا استعمال کرتے ہوئے۔

اگر آپ ایک ایسی ویب ایپ بنانا چاہتے ہیں جو سرور کے ساتھ کام نہ کرے، تو یہ بھی ممکن ہے کہ IndexedDB API کا استعمال کرتے ہوئے کلائنٹ پر ایک ڈیٹا بیس بنایا جائے۔ یہ ایک اعلی درجے کے استعمال کے معاملات کے لیے مخصوص ہے یا اگر آپ کو بڑی مقدار میں ڈیٹا ذخیرہ کرنے کی ضرورت ہو، کیونکہ اسے استعمال کرنا زیادہ پیچیدہ ہے۔

کام

ہم چاہتے ہیں کہ ہمارے صارفین لاگ ان رہیں جب تک کہ وہ واضح طور پر Logout بٹن پر کلک نہ کریں، لہذا ہم اکاؤنٹ کے ڈیٹا کو ذخیرہ کرنے کے لیے 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'); کو 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 }
};

اب ڈیش بورڈ کو ریفریش کرنے کی کوشش کریں، یہ اپ ڈیٹ شدہ اکاؤنٹ ڈیٹا کو ظاہر کرنا چاہیے۔


🚀 چیلنج

اب جب کہ ہم ہر بار ڈیش بورڈ لوڈ ہونے پر اکاؤنٹ کے ڈیٹا کو دوبارہ لوڈ کرتے ہیں، کیا آپ کو لگتا ہے کہ ہمیں پورے اکاؤنٹ کے ڈیٹا کو برقرار رکھنے کی "Add transaction" ڈائیلاگ نافذ کریں

یہاں ایک مثال ہے جو اس کام کو مکمل کرنے کے بعد حاصل ہوگی:

اسکرین شاٹ جو "Add transaction" ڈائیلاگ کی ایک مثال دکھا رہا ہے


ڈسکلیمر:
یہ دستاویز AI ترجمہ سروس Co-op Translator کا استعمال کرتے ہوئے ترجمہ کی گئی ہے۔ ہم درستگی کے لیے کوشش کرتے ہیں، لیکن براہ کرم آگاہ رہیں کہ خودکار ترجمے میں غلطیاں یا غیر درستیاں ہو سکتی ہیں۔ اصل دستاویز کو اس کی اصل زبان میں مستند ذریعہ سمجھا جانا چاہیے۔ اہم معلومات کے لیے، پیشہ ور انسانی ترجمہ کی سفارش کی جاتی ہے۔ ہم اس ترجمے کے استعمال سے پیدا ہونے والی کسی بھی غلط فہمی یا غلط تشریح کے ذمہ دار نہیں ہیں۔