مع نمو تطبيقات الويب، يصبح من الصعب تتبع تدفقات البيانات. أي كود يحصل على البيانات؟ أي صفحة تستهلكها؟ أين ومتى يجب تحديثها؟ من السهل أن ينتهي بك الأمر بكود فوضوي يصعب صيانته. هذا الأمر يصبح أكثر تعقيدًا عندما تحتاج إلى مشاركة البيانات بين صفحات مختلفة في التطبيق، مثل بيانات المستخدم. مفهوم *إدارة الحالة* كان دائمًا موجودًا في جميع أنواع البرامج، ولكن مع زيادة تعقيد تطبيقات الويب، أصبح نقطة أساسية يجب التفكير فيها أثناء التطوير.
مع نمو تطبيقات الويب، يصبح من الصعب تتبع تدفقات البيانات. أي كود يحصل على البيانات، أي صفحة تستهلكها، أين ومتى تحتاج إلى التحديث... من السهل أن ينتهي بك الأمر بكود فوضوي يصعب صيانته. هذا الأمر يصبح أكثر تعقيدًا عندما تحتاج إلى مشاركة البيانات بين صفحات مختلفة في التطبيق، مثل بيانات المستخدم. مفهوم *إدارة الحالة* كان موجودًا دائمًا في جميع أنواع البرامج، ولكن مع زيادة تعقيد تطبيقات الويب أصبح نقطة أساسية يجب التفكير فيها أثناء التطوير.
في هذا الجزء الأخير، سنراجع التطبيق الذي قمنا ببنائه لإعادة التفكير في كيفية إدارة الحالة، مما يسمح بدعم تحديث المتصفح في أي وقت، والحفاظ على البيانات عبر جلسات المستخدم.
@ -23,7 +23,7 @@ CO_OP_TRANSLATOR_METADATA:
يجب أن تكون قد أكملت جزء [جلب البيانات](../3-data/README.md) من تطبيق الويب لهذه الدرس. كما تحتاج إلى تثبيت [Node.js](https://nodejs.org) وتشغيل [خادم API](../api/README.md) محليًا حتى تتمكن من إدارة بيانات الحساب.
يمكنك اختبار أن الخادم يعمل بشكل صحيح عن طريق تنفيذ هذا الأمر في الطرفية:
يمكنك اختبار تشغيل الخادم بشكل صحيح عن طريق تنفيذ هذا الأمر في الطرفية:
```sh
curl http://localhost:5000/api
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## إعادة التفكير في إدارة الحالة
في [الدرس السابق](../3-data/README.md)، قدمنا مفهومًا أساسيًا للحالة في تطبيقنا باستخدام المتغير العام `account` الذي يحتوي على بيانات البنك للمستخدم الذي قام بتسجيل الدخول حاليًا. ومع ذلك، فإن التنفيذ الحالي لدينا به بعض العيوب. حاول تحديث الصفحة عندما تكون في لوحة التحكم. ماذا يحدث؟
في [الدرس السابق](../3-data/README.md)، قدمنا مفهومًا أساسيًا للحالة في تطبيقنا باستخدام المتغير العام `account` الذي يحتوي على بيانات البنك للمستخدم الذي قام بتسجيل الدخول حاليًا. ومع ذلك، فإن التنفيذ الحالي لدينا يحتوي على بعض العيوب. حاول تحديث الصفحة عندما تكون في لوحة التحكم. ماذا يحدث؟
هناك 3 مشكلات في الكود الحالي:
هناك 3 مشاكل في الكود الحالي:
- الحالة غير مستمرة، حيث يؤدي تحديث المتصفح إلى إعادتك إلى صفحة تسجيل الدخول.
- هناك وظائف متعددة تعدل الحالة. مع نمو التطبيق، قد يصبح من الصعب تتبع التغييرات ومن السهل نسيان تحديث واحدة منها.
- الحالة لا يتم تنظيفها، لذا عند النقر على *تسجيل الخروج*، تظل بيانات الحساب موجودة حتى وأنت في صفحة تسجيل الدخول.
- الحالة غير محفوظة، حيث يؤدي تحديث المتصفح إلى إعادتك إلى صفحة تسجيل الدخول.
- هناك وظائف متعددة تعدل الحالة. مع نمو التطبيق، يمكن أن يصبح من الصعب تتبع التغييرات ومن السهل نسيان تحديث واحدة.
- الحالة غير مُنظفة، لذا عندما تنقر على *تسجيل الخروج* تبقى بيانات الحساب موجودة حتى وأنت في صفحة تسجيل الدخول.
يمكننا تحديث الكود لمعالجة هذه المشكلات واحدة تلو الأخرى، ولكن ذلك سيؤدي إلى تكرار الكود وجعل التطبيق أكثر تعقيدًا وصعوبة في الصيانة. أو يمكننا التوقف لبضع دقائق وإعادة التفكير في استراتيجيتنا.
يمكننا تحديث الكود لمعالجة هذه المشاكل واحدة تلو الأخرى، ولكن ذلك سيؤدي إلى تكرار الكود وجعل التطبيق أكثر تعقيدًا وصعوبة في الصيانة. أو يمكننا التوقف لبضع دقائق وإعادة التفكير في استراتيجيتنا.
> ما هي المشكلات التي نحاول حلها هنا؟
> ما هي المشاكل التي نحاول حلها هنا؟
[إدارة الحالة](https://en.wikipedia.org/wiki/State_management) تدور حول إيجاد نهج جيد لحل هاتين المشكلتين بالتحديد:
- كيف يمكن الحفاظ على تدفقات البيانات في التطبيق بشكل مفهوم؟
- كيف يمكن الحفاظ على تزامن بيانات الحالة دائمًا مع واجهة المستخدم (والعكس صحيح)؟
- كيف يمكن الحفاظ على بيانات الحالة متزامنة دائمًا مع واجهة المستخدم (والعكس صحيح)؟
بمجرد أن تعتني بهاتين المشكلتين، قد يتم حل أي مشكلات أخرى لديك بالفعل أو تصبح أسهل في الحل. هناك العديد من النهج الممكنة لحل هذه المشكلات، ولكننا سنختار حلاً شائعًا يتكون من **مركزة البيانات وطرق تغييرها**. ستتدفق البيانات على النحو التالي:
بمجرد أن تعتني بهذه النقاط، قد يتم حل أي مشاكل أخرى لديك بالفعل أو تصبح أسهل في الحل. هناك العديد من النهج الممكنة لحل هذه المشاكل، ولكننا سنختار حلاً شائعًا يتكون من **مركزية البيانات وطرق تغييرها**. تدفقات البيانات ستكون كالتالي:

> لن نغطي هنا الجزء الذي يتم فيه تحديث العرض تلقائيًا بواسطة البيانات، حيث يرتبط بمفاهيم أكثر تقدمًا في [البرمجة التفاعلية](https://en.wikipedia.org/wiki/Reactive_programming). إنه موضوع جيد للمتابعة إذا كنت ترغب في التعمق.
> لن نغطي هنا الجزء الذي يتم فيه تحديث العرض تلقائيًا بواسطة البيانات، حيث إنه مرتبط بمفاهيم أكثر تقدمًا في [البرمجة التفاعلية](https://en.wikipedia.org/wiki/Reactive_programming). إنه موضوع جيد للمتابعة إذا كنت ترغب في التعمق.
✅ هناك العديد من المكتبات التي تقدم نهجًا مختلفًا لإدارة الحالة، مثل [Redux](https://redux.js.org) الذي يُعد خيارًا شائعًا. ألقِ نظرة على المفاهيم والأنماط المستخدمة، حيث إنها غالبًا ما تكون طريقة جيدة لتعلم المشكلات المحتملة التي قد تواجهها في تطبيقات الويب الكبيرة وكيف يمكن حلها.
✅ هناك العديد من المكتبات التي تقدم نهجًا مختلفًا لإدارة الحالة، مثل [Redux](https://redux.js.org) التي تعتبر خيارًا شائعًا. ألقِ نظرة على المفاهيم والأنماط المستخدمة حيث إنها غالبًا ما تكون طريقة جيدة لتعلم المشاكل المحتملة التي قد تواجهها في تطبيقات الويب الكبيرة وكيف يمكن حلها.
### المهمة
@ -75,9 +75,9 @@ let state = {
};
```
الفكرة هي *مركزة* جميع بيانات التطبيق في كائن حالة واحد. لدينا فقط `account` في الحالة الآن، لذا لن يتغير الكثير، ولكنه يفتح المجال للتطورات المستقبلية.
الفكرة هي *مركزية* جميع بيانات التطبيق في كائن حالة واحد. لدينا فقط `account` في الحالة الآن لذا لا يتغير الكثير، ولكنه يفتح المجال للتطورات المستقبلية.
علينا أيضًا تحديث الوظائف التي تستخدمه. في وظائف `register()` و`login()`، استبدل `account = ...` بـ `state.account = ...`;
علينا أيضًا تحديث الوظائف التي تستخدمه. في وظائف `register()` و`login()`، استبدل `account = ...` بـ `state.account = ...`;
في بداية وظيفة `updateDashboard()`، أضف هذا السطر:
@ -89,9 +89,9 @@ const account = state.account;
## تتبع تغييرات البيانات
الآن بعد أن وضعنا كائن `state` لتخزين بياناتنا، الخطوة التالية هي مركزة التحديثات. الهدف هو جعل تتبع أي تغييرات ومتى تحدث أسهل.
الآن بعد أن وضعنا كائن `state` لتخزين بياناتنا، الخطوة التالية هي مركزية التحديثات. الهدف هو جعل تتبع أي تغييرات ومتى تحدث أسهل.
لتجنب إجراء تغييرات على كائن `state`، من الجيد أيضًا اعتباره [*غير قابل للتغيير*](https://en.wikipedia.org/wiki/Immutable_object)، مما يعني أنه لا يمكن تعديله على الإطلاق. هذا يعني أيضًا أنه يجب عليك إنشاء كائن حالة جديد إذا كنت تريد تغيير أي شيء فيه. من خلال القيام بذلك، تبني حماية ضد [التأثيرات الجانبية](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) غير المرغوب فيها، وتفتح المجال لإضافة ميزات جديدة في تطبيقك مثل تنفيذ التراجع/الإعادة، مع جعل عملية تصحيح الأخطاء أسهل. على سبيل المثال، يمكنك تسجيل كل تغيير يتم إجراؤه على الحالة والاحتفاظ بسجل للتغييرات لفهم مصدر الخطأ.
لتجنب إجراء تغييرات على كائن `state`، من الجيد أيضًا اعتباره [*غير قابل للتغيير*](https://en.wikipedia.org/wiki/Immutable_object)، مما يعني أنه لا يمكن تعديله على الإطلاق. هذا يعني أيضًا أنه يجب عليك إنشاء كائن حالة جديد إذا كنت تريد تغيير أي شيء فيه. من خلال القيام بذلك، تبني حماية ضد [التأثيرات الجانبية](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) غير المرغوب فيها، وتفتح المجال لإمكانيات ميزات جديدة في التطبيق مثل تنفيذ التراجع/الإعادة، بينما تجعل عملية التصحيح أسهل. على سبيل المثال، يمكنك تسجيل كل تغيير يتم إجراؤه على الحالة والاحتفاظ بسجل للتغييرات لفهم مصدر الخطأ.
في JavaScript، يمكنك استخدام [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) لإنشاء نسخة غير قابلة للتغيير من الكائن. إذا حاولت إجراء تغييرات على كائن غير قابل للتغيير، سيتم رفع استثناء.
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
في هذه الوظيفة، نقوم بإنشاء كائن حالة جديد ونسخ البيانات من الحالة السابقة باستخدام [عامل الانتشار (`...`)](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). ثم نقوم بتجاوز خاصية معينة من كائن الحالة بالبيانات الجديدة باستخدام [ترميز الأقواس](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` للتعيين. أخيرًا، نقوم بقفل الكائن لمنع التعديلات باستخدام `Object.freeze()`. لدينا فقط خاصية `account` مخزنة في الحالة الآن، ولكن مع هذا النهج يمكنك إضافة العديد من الخصائص كما تحتاج في الحالة.
في هذه الوظيفة، نقوم بإنشاء كائن حالة جديد ونسخ البيانات من الحالة السابقة باستخدام [عامل الانتشار (`...`)](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). ثم نقوم بتجاوز خاصية معينة من كائن الحالة بالبيانات الجديدة باستخدام [صيغة الأقواس](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` للتعيين. أخيرًا، نقوم بتجميد الكائن لمنع التعديلات باستخدام `Object.freeze()`. لدينا فقط خاصية `account` مخزنة في الحالة الآن، ولكن مع هذا النهج يمكنك إضافة أي عدد من الخصائص التي تحتاجها في الحالة.
سنقوم أيضًا بتحديث تهيئة `state` للتأكد من أن الحالة الأولية مجمدة أيضًا:
@ -126,13 +126,13 @@ let state = Object.freeze({
updateState('account', result);
```
افعل الشيء نفسه مع وظيفة `login`، استبدل `state.account = data;` بـ:
قم بنفس الشيء مع وظيفة `login`، استبدل `state.account = data;` بـ:
```js
updateState('account', data);
```
سنستغل الفرصة الآن لإصلاح مشكلة عدم مسح بيانات الحساب عند النقر على *تسجيل الخروج*.
سنستغل الفرصة الآن لإصلاح مشكلة عدم تنظيف بيانات الحساب عندما ينقر المستخدم على *تسجيل الخروج*.
قم بإنشاء وظيفة جديدة `logout()`:
@ -143,31 +143,31 @@ function logout() {
}
```
في `updateDashboard()`، استبدل إعادة التوجيه `return navigate('/login');` بـ `return logout();`
في `updateDashboard()`، استبدل إعادة التوجيه `return navigate('/login');` بـ `return logout()`;
حاول تسجيل حساب جديد، تسجيل الخروج ثم تسجيل الدخول مرة أخرى للتحقق من أن كل شيء لا يزال يعمل بشكل صحيح.
> نصيحة: يمكنك إلقاء نظرة على جميع تغييرات الحالة عن طريق إضافة `console.log(state)` في أسفل`updateState()` وفتح وحدة التحكم في أدوات تطوير المتصفح.
> نصيحة: يمكنك الاطلاع على جميع تغييرات الحالة عن طريق إضافة `console.log(state)` في نهاية`updateState()` وفتح وحدة التحكم في أدوات تطوير المتصفح.
## الحفاظ على الحالة
تحتاج معظم تطبيقات الويب إلى الحفاظ على البيانات لتعمل بشكل صحيح. عادةً ما يتم تخزين جميع البيانات الحرجة في قاعدة بيانات ويتم الوصول إليها عبر خادم API، مثل بيانات حساب المستخدم في حالتنا. ولكن في بعض الأحيان، من المفيد أيضًا الحفاظ على بعض البيانات في تطبيق العميل الذي يعمل في متصفحك، لتحسين تجربة المستخدم أو تحسين أداء التحميل.
تحتاج معظم تطبيقات الويب إلى الحفاظ على البيانات لتتمكن من العمل بشكل صحيح. يتم عادةً تخزين جميع البيانات المهمة في قاعدة بيانات ويتم الوصول إليها عبر خادم API، مثل بيانات حساب المستخدم في حالتنا. ولكن في بعض الأحيان، من المفيد أيضًا الحفاظ على بعض البيانات في تطبيق العميل الذي يعمل في المتصفح، لتحسين تجربة المستخدم أو تحسين أداء التحميل.
عندما تريد الحفاظ على البيانات في متصفحك، هناك بعض الأسئلة المهمة التي يجب أن تسألها لنفسك:
عندما تريد الحفاظ على البيانات في المتصفح، هناك بعض الأسئلة المهمة التي يجب أن تسألها لنفسك:
- *هل البيانات حساسة؟* يجب تجنب تخزين أي بيانات حساسة على العميل، مثل كلمات مرور المستخدم.
- *إلى متى تحتاج إلى الاحتفاظ بهذه البيانات؟* هل تخطط للوصول إلى هذه البيانات فقط للجلسة الحالية أم تريد تخزينها إلى الأبد؟
هناك طرق متعددة لتخزين المعلومات داخل تطبيق ويب، بناءً على ما تريد تحقيقه. على سبيل المثال، يمكنك استخدام عناوين URL لتخزين استعلام بحث، وجعله قابلاً للمشاركة بين المستخدمين. يمكنك أيضًا استخدام [ملفات تعريف الارتباط HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) إذا كانت البيانات تحتاج إلى المشاركة مع الخادم، مثل معلومات [المصادقة](https://en.wikipedia.org/wiki/Authentication).
هناك طرق متعددة لتخزين المعلومات داخل تطبيق الويب، اعتمادًا على ما تريد تحقيقه. على سبيل المثال، يمكنك استخدام عناوين URL لتخزين استعلام البحث، وجعله قابلاً للمشاركة بين المستخدمين. يمكنك أيضًا استخدام [ملفات تعريف الارتباط HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) إذا كانت البيانات تحتاج إلى المشاركة مع الخادم، مثل معلومات [المصادقة](https://en.wikipedia.org/wiki/Authentication).
خيار آخر هو استخدام واحدة من العديد من واجهات برمجة التطبيقات للمتصفح لتخزين البيانات. اثنتان منها مثيرتان للاهتمام بشكل خاص:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): مخزن [مفتاح/قيمة](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) يسمح بالحفاظ على البيانات الخاصة بموقع الويب الحالي عبر جلسات مختلفة. البيانات المحفوظة فيه لا تنتهي صلاحيتها أبدًا.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): يعمل بنفس طريقة `localStorage`باستثناء أن البيانات المخزنة فيه يتم مسحها عند انتهاء الجلسة (عند إغلاق المتصفح).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): يعمل بنفس طريقة `localStorage`إلا أن البيانات المخزنة فيه يتم مسحها عند انتهاء الجلسة (عند إغلاق المتصفح).
لاحظ أن كلا هاتين الواجهتين تسمح فقط بتخزين [سلاسل نصية](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). إذا كنت تريد تخزين كائنات معقدة، ستحتاج إلى تسلسلها إلى تنسيق [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) باستخدام [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
لاحظ أن كلا واجهتي برمجة التطبيقات تسمح فقط بتخزين [النصوص](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). إذا كنت تريد تخزين كائنات معقدة، ستحتاج إلى تسلسلها إلى صيغة [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) باستخدام [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ إذا كنت تريد إنشاء تطبيق ويب لا يعمل مع خادم، فمن الممكن أيضًا إنشاء قاعدة بيانات على العميل باستخدام واجهة برمجة التطبيقات [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). هذا الخيار مخصص للحالات المتقدمة أو إذا كنت بحاجة إلى تخزين كمية كبيرة من البيانات، حيث إنه أكثر تعقيدًا في الاستخدام.
✅ إذا كنت تريد إنشاء تطبيق ويب لا يعمل مع خادم، من الممكن أيضًا إنشاء قاعدة بيانات على العميل باستخدام واجهة برمجة التطبيقات [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). هذه مخصصة لحالات الاستخدام المتقدمة أو إذا كنت بحاجة إلى تخزين كمية كبيرة من البيانات، حيث إنها أكثر تعقيدًا في الاستخدام.
بهذا، سيتم الحفاظ على بيانات حساب المستخدم وتحديثها دائمًا حيث قمنا بمركزة جميع تحديثات الحالة مسبقًا. هذا هو المكان الذي نبدأ فيه الاستفادة من جميع عمليات إعادة الهيكلة السابقة 🙂.
بهذا، سيتم الحفاظ على بيانات حساب المستخدم وتحديثها دائمًا كما قمنا بمركزية جميع تحديثات الحالة سابقًا. هنا نبدأ في الاستفادة من جميع عمليات إعادة الهيكلة السابقة 🙂.
نظرًا لأن البيانات محفوظة، علينا أيضًا استعادتها عند تحميل التطبيق. بما أننا سنبدأ في الحصول على المزيد من كود التهيئة، قد يكون من الجيد إنشاء وظيفة جديدة `init`، تتضمن أيضًا الكود السابق في أسفل`app.js`:
نظرًا لأن البيانات محفوظة، علينا أيضًا أن نهتم باستعادتها عند تحميل التطبيق. نظرًا لأننا سنبدأ في الحصول على المزيد من كود التهيئة، قد يكون من الجيد إنشاء وظيفة جديدة `init`، التي تتضمن أيضًا الكود السابق في نهاية`app.js`:
```js
function init() {
@ -202,11 +202,11 @@ function init() {
init();
```
هنا نستعيد البيانات المحفوظة، وإذا كانت موجودة نقوم بتحديث الحالة وفقًا لذلك. من المهم القيام بذلك *قبل* تحديث المسار، حيث قد يكون هناك كود يعتمد على الحالة أثناء تحديث الصفحة.
هنا نستعيد البيانات المحفوظة، وإذا كان هناك أي بيانات نقوم بتحديث الحالة وفقًا لذلك. من المهم القيام بذلك *قبل* تحديث المسار، حيث قد يكون هناك كود يعتمد على الحالة أثناء تحديث الصفحة.
يمكننا أيضًا جعل صفحة *لوحة التحكم* الصفحة الافتراضية لتطبيقنا، حيث إننا الآن نحافظ على بيانات الحساب. إذا لم يتم العثور على بيانات، فإن لوحة التحكم تتولى إعادة التوجيه إلى صفحة *تسجيل الدخول* على أي حال. في `updateRoute()`، استبدل القيمة الافتراضية`return navigate('/login');` بـ `return navigate('/dashboard');`.
يمكننا أيضًا جعل صفحة *لوحة التحكم* الصفحة الافتراضية لتطبيقنا، حيث إننا الآن نحافظ على بيانات الحساب. إذا لم يتم العثور على بيانات، فإن لوحة التحكم تتولى إعادة التوجيه إلى صفحة *تسجيل الدخول* على أي حال. في `updateRoute()`، استبدل الخيار الافتراضي`return navigate('/login');` بـ `return navigate('/dashboard');`.
الآن قم بتسجيل الدخول إلى التطبيق وحاول تحديث الصفحة. يجب أن تبقى في لوحة التحكم. مع هذا التحديث، قمنا بمعالجة جميع مشكلاتنا الأولية...
الآن قم بتسجيل الدخول إلى التطبيق وحاول تحديث الصفحة. يجب أن تبقى في لوحة التحكم. مع هذا التحديث، قمنا بمعالجة جميع مشاكلنا الأولية...
## تحديث البيانات
@ -273,22 +273,22 @@ const routes = {
## 🚀 التحدي
الآن بعد أن قمنا بإعادة تحميل بيانات الحساب في كل مرة يتم فيها تحميل لوحة التحكم، هل تعتقد أننا ما زلنا بحاجة إلى الحفاظ على *جميع بيانات الحساب*؟
الآن بعد أن نقوم بإعادة تحميل بيانات الحساب في كل مرة يتم فيها تحميل لوحة التحكم، هل تعتقد أننا ما زلنا بحاجة إلى الحفاظ على *كل بيانات الحساب*؟
حاول العمل معًا لتغيير ما يتم حفظه وتحميله من `localStorage` ليشمل فقط ما هو ضروري تمامًا لعمل التطبيق.
## اختبار ما بعد المحاضرة
[اختبار ما بعد المحاضرة](https://ff-quizzes.netlify.app/web/quiz/48)
## الواجب
[تنفيذ مربع حوار "إضافة معاملة"](assignment.md)
إليك مثالاً على النتيجة بعد إكمال المهمة:
[تنفيذ نافذة "إضافة معاملة"](assignment.md)
إليك مثال على النتيجة بعد إكمال الواجب:


---
**إخلاء المسؤولية**:
تم ترجمة هذا المستند باستخدام خدمة الترجمة بالذكاء الاصطناعي [Co-op Translator](https://github.com/Azure/co-op-translator). بينما نسعى لتحقيق الدقة، يرجى العلم أن الترجمات الآلية قد تحتوي على أخطاء أو عدم دقة. يجب اعتبار المستند الأصلي بلغته الأصلية المصدر الرسمي. للحصول على معلومات حاسمة، يُوصى بالترجمة البشرية الاحترافية. نحن غير مسؤولين عن أي سوء فهم أو تفسيرات خاطئة تنشأ عن استخدام هذه الترجمة.
تم ترجمة هذا المستند باستخدام خدمة الترجمة بالذكاء الاصطناعي [Co-op Translator](https://github.com/Azure/co-op-translator). بينما نسعى لتحقيق الدقة، يرجى العلم أن الترجمات الآلية قد تحتوي على أخطاء أو عدم دقة. يجب اعتبار المستند الأصلي بلغته الأصلية المصدر الرسمي. للحصول على معلومات حاسمة، يُوصى بالترجمة البشرية الاحترافية. نحن غير مسؤولين عن أي سوء فهم أو تفسيرات خاطئة ناتجة عن استخدام هذه الترجمة.
С разрастването на уеб приложението става предизвикателство да се следят всички потоци от данни. Кой код получава данните, коя страница ги използва, къде и кога трябва да бъдат актуализирани... лесно е да се стигне до объркан код, който е труден за поддръжка. Това е особено вярно, когато трябва да споделяте данни между различни страници на приложението, например данни за потребителя. Концепцията за *управление на състоянието* винаги е съществувала във всякакви програми, но с нарастващата сложност на уеб приложенията тя вече е ключова точка за обмисляне по време на разработката.
С разрастването на уеб приложението става предизвикателство да се следят всички потоци от данни. Кой код получава данните, коя страница ги използва, къде и кога трябва да бъдат актуализирани... лесно е да се стигне до объркан код, който е труден за поддръжка. Това е особено вярно, когато трябва да споделяте данни между различни страници на приложението си, например данни за потребителя. Концепцията за *управление на състоянието* винаги е съществувала във всички видове програми, но с нарастващата сложност на уеб приложенията тя вече е ключова точка за обмисляне по време на разработката.
В тази последна част ще разгледаме приложението, което създадохме, за да преосмислим как се управлява състоянието, като позволим поддръжка на обновяване на браузъра във всеки момент и запазване на данните между потребителските сесии.
@ -51,11 +51,11 @@ curl http://localhost:5000/api
- Как да направим потоците от данни в приложението разбираеми?
- Как да поддържаме данните за състоянието винаги синхронизирани с потребителския интерфейс (и обратно)?
След като се погрижите за тези проблеми, всички други проблеми, които може да имате, или ще бъдат вече решени, или ще станат по-лесни за решаване. Има много възможни подходи за решаване на тези проблеми, но ние ще използваме общо решение, което се състои в **централизиране на данните и начините за тяхната промяна**. Потокът от данни би изглеждал така:
След като се погрижите за тези проблеми, всички други проблеми, които може да имате, може вече да са решени или да са станали по-лесни за решаване. Има много възможни подходи за решаване на тези проблеми, но ще използваме общо решение, което се състои в **централизиране на данните и начините за тяхната промяна**. Потокът от данни би изглеждал така:

> Тук няма да разглеждаме частта, където данните автоматично задействат актуализацията на изгледа, тъй като тя е свързана с по-напреднали концепции на [Реактивното програмиране](https://en.wikipedia.org/wiki/Reactive_programming). Това е добра тема за последващо задълбочено изучаване.
> Тук няма да разглеждаме частта, в която данните автоматично задействат актуализацията на изгледа, тъй като тя е свързана с по-напреднали концепции на [Реактивното програмиране](https://en.wikipedia.org/wiki/Reactive_programming). Това е добра тема за последващо задълбочаване.
✅ Има много библиотеки с различни подходи за управление на състоянието, като [Redux](https://redux.js.org) е популярна опция. Разгледайте концепциите и моделите, които се използват, тъй като често е добър начин да научите какви потенциални проблеми може да срещнете в големи уеб приложения и как те могат да бъдат решени.
@ -91,7 +91,7 @@ const account = state.account;
Сега, когато сме поставили обекта `state`, за да съхраняваме данните си, следващата стъпка е да централизиране актуализациите. Целта е да се улесни проследяването на всякакви промени и кога те се случват.
За да избегнем промени в обекта `state`, също е добра практика да го считаме за [*неизменяем*](https://en.wikipedia.org/wiki/Immutable_object), което означава, че той изобщо не може да бъде модифициран. Това също означава, че трябва да създадете нов обект за състояние, ако искате да промените нещо в него. Като правите това, изграждате защита срещу потенциално нежелани [странични ефекти](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) и отваряте възможности за нови функции в приложението си, като например внедряване на undo/redo, като същевременно улеснявате дебъгването. Например, можете да регистрирате всяка промяна, направена в състоянието, и да запазите история на промените, за да разберете източника на грешка.
За да избегнем промени в обекта `state`, също е добра практика да го считаме за [*неизменяем*](https://en.wikipedia.org/wiki/Immutable_object), което означава, че той не може да бъде модифициран изобщо. Това също означава, че трябва да създадете нов обект за състояние, ако искате да промените нещо в него. Като правите това, изграждате защита срещу потенциално нежелани [странични ефекти](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) и отваряте възможности за нови функции в приложението си, като например имплементиране на undo/redo, като същевременно улеснявате дебъгването. Например, можете да регистрирате всяка промяна, направена в състоянието, и да запазите история на промените, за да разберете източника на грешка.
В JavaScript можете да използвате [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze), за да създадете неизменяема версия на обект. Ако се опитате да направите промени в неизменяем обект, ще бъде хвърлено изключение.
@ -112,7 +112,7 @@ function updateState(property, newData) {
В тази функция създаваме нов обект за състояние и копираме данни от предишното състояние, използвайки [*оператора за разпръскване (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). След това презаписваме конкретно свойство на обекта за състояние с новите данни, използвайки [нотацията с квадратни скоби](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` за присвояване. Накрая заключваме обекта, за да предотвратим модификации, използвайки `Object.freeze()`. Засега имаме само свойството `account`, съхранено в състоянието, но с този подход можете да добавите толкова свойства, колкото са ви необходими.
Също така ще актуализираме инициализацията на `state`, за да сеуверим, че началното състояние също е заключено:
Също така ще актуализираме инициализацията на `state`, за да смесигурни, че началното състояние също е заключено:
```js
let state = Object.freeze({
@ -147,31 +147,31 @@ function logout() {
Опитайте да регистрирате нов акаунт, да излезете и да влезете отново, за да проверите дали всичко все още работи правилно.
> Съвет: можете да разгледате всички промени в състоянието, като добавите `console.log(state)` в края на `updateState()` и отворите конзолата в инструментите за разработка на браузъра.
> Съвет: можете да разгледате всички промени в състоянието, като добавите `console.log(state)` в края на `updateState()` и отворите конзолата в инструментите за разработка на браузъра си.
## Запазване на състоянието
Повечето уеб приложения трябва да запазват данни, за да могат да работят правилно. Всички критични данни обикновено се съхраняват в база данни и се достъпват чрез API сървър, като например данните за потребителския акаунт в нашия случай. Но понякога е интересно да се запазят някои данни в клиентското приложение, което работи в браузъра, за по-добро потребителско изживяване или за подобряване на производителността при зареждане.
Повечето уеб приложения трябва да запазват данни, за да могат да работят правилно. Всички критични данни обикновено се съхраняват в база данни и се достъпват чрез API на сървъра, като например данните за потребителския акаунт в нашия случай. Но понякога е интересно да се запазят някои данни в клиентското приложение, което работи в браузъра, за по-добро потребителско изживяване или за подобряване на производителността при зареждане.
Когато искате да запазите данни в браузъра, има няколко важни въпроса, които трябва да си зададете:
Когато искате да запазите данни в браузъра си, има няколко важни въпроса, които трябва да си зададете:
- *Данните чувствителни ли са?* Трябва да избягвате съхраняването на чувствителни данни в клиента, като например пароли на потребители.
- *За колко време трябва да запазите тези данни?* Планирате ли да достъпите тези данни само за текущата сесия или искате те да бъдат съхранени завинаги?
- *Колко дълго трябва да запазите тези данни?* Планирате ли да достъпвате тези данни само за текущата сесия или искате те да бъдат съхранени завинаги?
Има множество начини за съхраняване на информация в уеб приложение, в зависимост от това, което искате да постигнете. Например, можете да използвате URL адресите, за да съхранявате заявка за търсене и да я направите споделима между потребителите. Можете също така да използвате [HTTP бисквитки](https://developer.mozilla.org/docs/Web/HTTP/Cookies), ако данните трябва да бъдат споделени със сървъра, като информация за [автентикация](https://en.wikipedia.org/wiki/Authentication).
Има множество начини за съхраняване на информация в уеб приложение, в зависимост от това, което искате да постигнете. Например, можете да използвате URL адресите, за да съхранявате заявка за търсене и да я направите споделима между потребителите. Можете също така да използвате [HTTP бисквитки](https://developer.mozilla.org/docs/Web/HTTP/Cookies), ако данните трябва да бъдат споделени със сървъра, като например информация за [автентикация](https://en.wikipedia.org/wiki/Authentication).
Друга опция е да използвате една от многото API на браузъра за съхраняване на данни. Две от тях са особено интересни:
Друга опция е да използвате един от многото API на браузъра за съхраняване на данни. Два от тях са особено интересни:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value хранилище](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), което позволява запазване на данни, специфични за текущия уеб сайт, между различни сесии. Данните, запазени в него, никога не изтичат.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): работи по същия начин като `localStorage`, с изключение на това, че данните, съхранени в него, се изчистват, когато сесията приключи (когато браузърът се затвори).
Имайте предвид, че и двата API позволяват съхраняване само на [стрингове](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ако искате да съхранявате сложни обекти, ще трябва да ги сериализирате във формат [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON), използвайки [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Ако искате да създадете уеб приложение, което не работи със сървър, е възможно да създадете база данни на клиента, използвайки API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Това е запазено за напреднали случаи на употреба или ако трябва да съхранявате значително количество данни, тъй като е по-сложно за използване.
✅ Ако искате да създадете уеб приложение, което не работи със сървър, е възможно да създадете база данни на клиента, използвайки [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Това е запазено за напреднали случаи на употреба или ако трябва да съхранявате значително количество данни, тъй като е по-сложно за използване.
### Задача
Искаме нашите потребители да останат влезли, докато не кликнат изрично върху бутона *Изход*, така че ще използваме `localStorage`, за да съхраняваме данните за акаунта. Първо, нека дефинираме ключ, който ще използваме за съхраняване на нашите данни.
Искаме нашите потребители да останат влезли, докато не кликнат изрично върху бутона *Изход*, затова ще използваме `localStorage`, за да съхраняваме данните за акаунта. Първо, нека дефинираме ключ, който ще използваме за съхраняване на данните.
С това данните за потребителския акаунт ще бъдат запазени и винаги актуални, както централизирахме преди всички актуализации на състоянието. Тук започваме да се възползваме от всички предишни рефакторирания 🙂.
С това данните за потребителския акаунт ще бъдат запазени и винаги актуални, тъй като преди това централизирахме всички актуализации на състоянието. Тук започваме да се възползваме от всички предишни рефакторирания 🙂.
Тъй като данните се запазват, трябва също да се погрижим за тяхното възстановяване, когато приложението се зареди. Тъй като ще започнем да имаме повече код за инициализация, може би е добра идея да създадем нова функция `init`, която също включва предишния код в края на `app.js`:
Тъй като данните се запазват, трябва също да се погрижим за тяхното възстановяване, когато приложението се зареди. Тъй като започваме да имаме повече код за инициализация, може би е добра идея да създадем нова функция `init`, която включва и предишния код в края на `app.js`:
```js
function init() {
@ -204,13 +204,13 @@ init();
Тук извличаме запазените данни и ако има такива, актуализираме състоянието съответно. Важно е да направим това *преди* актуализирането на маршрута, тъй като може да има код, който разчита на състоянието по време на актуализацията на страницата.
Също така можем да направим страницата *Табло* основната страница на нашето приложение, тъй като сега запазваме данните за акаунта. Ако не се намерят данни, таблото се грижи за пренасочването към страницата *Вход* така или иначе. В`updateRoute()` заменете резервния `return navigate('/login');`с`return navigate('/dashboard');`.
Също така можем да направим страницата *Табло* основната страница на нашето приложение, тъй като сега запазваме данните за акаунта. Ако не се намерят данни, таблото се грижи за пренасочването към страницата *Вход* така или иначе. В`updateRoute()` заменете резервния вариант `return navigate('/login');`с`return navigate('/dashboard');`.
Сега влезте в приложението и опитайте да обновите страницата. Трябва да останете на таблото. С тази актуализация сме се погрижили за всички първоначални проблеми...
## Обновяване на данните
...Но може би сме създали нов проблем. Опа!
...Но може би сме създали нов. Опа!
Отидете на таблото, използвайки акаунта `test`, след това изпълнете тази команда в терминала, за да създадете нова транзакция:
@ -223,7 +223,7 @@ curl --request POST \
Опитайте да обновите страницата на таблото в браузъра сега. Какво се случва? Виждате ли новата транзакция?
Състоянието се запазва безкрайно благодарение на `localStorage`, но това също означава, че то никога не се актуализира, докато не излезете от приложението и не влезете отново!
Състоянието се запазва безкрайно благодарение на `localStorage`, но това също означава, че никога не се актуализира, докато не излезете от приложението и не влезете отново!
Една възможна стратегия за решаване на този проблем е да презареждаме данните за акаунта всеки път, когато таблото се зарежда, за да избегнем остарели данни.
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Този метод проверява дали в момента сме влезли, след това презарежда данните за акаунта от сървъра.
Този метод проверява дали в момента сме влезли, след което презарежда данните за акаунта от сървъра.
Създайте друга функция, наречена `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Тази функция актуализира данните за акаунта, след товасе грижи за актуализирането на HTML на страницата на таблото. Това е, което трябва да извикаме, когато маршрутът на таблото се зареди. Актуализирайте дефиницията на маршрута с:
Тази функция актуализира данните за акаунта, след което се грижи за актуализирането на HTML на страницата на таблото. Това е, което трябва да извикаме, когато маршрутът на таблото се зареди. Актуализирайте дефиницията на маршрута с:
```js
const routes = {
@ -268,13 +268,17 @@ const routes = {
```
Опитайте да обновите таблото сега, то трябва да показва актуализираните данни за акаунта
[Реализиране на диалог "Добавяне на транзакция"](assignment.md)
[Тест след лекцията](https://ff-quizzes.netlify.app/web/quiz/48)
Ето примерен резултат след завършване на задачата:
## Задание

[Реализирайте диалогов прозорец "Добавяне на транзакция"](assignment.md)
Ето примерен резултат след изпълнение на заданието:

---
**Отказ от отговорност**:
Този документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматичните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия изходен език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален превод от човек. Ние не носим отговорност за каквито и да е недоразумения или погрешни интерпретации, произтичащи от използването на този превод.
Този документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.
# ব্যাংকিং অ্যাপ তৈরি করুন পার্ট৪: স্টেট ম্যানেজমেন্টের ধারণা
# একটি ব্যাংকিং অ্যাপ তৈরি করুন পর্ব৪: স্টেট ম্যানেজমেন্টের ধারণা
## প্রাক-লেকচার কুইজ
@ -15,15 +15,15 @@ CO_OP_TRANSLATOR_METADATA:
### ভূমিকা
যখন একটি ওয়েব অ্যাপ্লিকেশন বড় হয়, তখন সমস্ত ডেটা প্রবাহ ট্র্যাক করা একটি চ্যালেঞ্জ হয়ে দাঁড়ায়। কোন কোড ডেটা পায়, কোন পৃষ্ঠা এটি ব্যবহার করে, কোথায় এবং কখন এটি আপডেট করতে হবে...এটি সহজেই জটিল কোডে পরিণত হতে পারে যা রক্ষণাবেক্ষণ করা কঠিন। এটি বিশেষভাবে সত্য যখন আপনার অ্যাপের বিভিন্ন পৃষ্ঠার মধ্যে ডেটা শেয়ার করতে হয়, যেমন ব্যবহারকারীর তথ্য। *স্টেট ম্যানেজমেন্ট* ধারণাটি সব ধরনের প্রোগ্রামে সবসময়ই বিদ্যমান ছিল, কিন্তু ওয়েব অ্যাপগুলো ক্রমাগত জটিল হয়ে উঠার সাথে সাথে এটি এখন উন্নয়নের সময় একটি গুরুত্বপূর্ণ বিষয় হয়ে উঠেছে।
যখন একটি ওয়েব অ্যাপ্লিকেশন বড় হয়, তখন সমস্ত ডেটা প্রবাহ ট্র্যাক রাখা একটি চ্যালেঞ্জ হয়ে দাঁড়ায়। কোন কোড ডেটা পায়, কোন পৃষ্ঠা এটি ব্যবহার করে, কোথায় এবং কখন এটি আপডেট করতে হবে... সহজেই এমন কোড তৈরি হতে পারে যা বজায় রাখা কঠিন। এটি বিশেষত সত্য যখন আপনার অ্যাপের বিভিন্ন পৃষ্ঠার মধ্যে ডেটা শেয়ার করতে হয়, যেমন ব্যবহারকারীর ডেটা। *স্টেট ম্যানেজমেন্ট* ধারণাটি সব ধরণের প্রোগ্রামে সবসময়ই বিদ্যমান ছিল, তবে ওয়েব অ্যাপ্লিকেশনগুলির জটিলতা বাড়ার সাথে সাথে এটি এখন উন্নয়নের সময় একটি গুরুত্বপূর্ণ বিষয় হয়ে উঠেছে।
এই চূড়ান্ত অংশে, আমরা আমাদের তৈরি করা অ্যাপটি পুনর্বিবেচনা করব কিভাবে স্টেট পরিচালিত হয়, ব্রাউজার রিফ্রেশের জন্য যেকোনো সময় সমর্থন প্রদান করে এবং ব্যবহারকারীর সেশনের মধ্যে ডেটা সংরক্ষণ করে।
এই চূড়ান্ত অংশে, আমরা আমাদের তৈরি করা অ্যাপটি পুনর্বিবেচনা করব কিভাবে স্টেট পরিচালিত হয়, ব্রাউজার রিফ্রেশের জন্য যেকোনো সময় সমর্থন প্রদান করে এবং ব্যবহারকারীর সেশনের মধ্যে ডেটা সংরক্ষণ করে।
### পূর্বশর্ত
এই পাঠের জন্য আপনাকে ওয়েব অ্যাপের [ডেটা ফেচিং](../3-data/README.md) অংশ সম্পন্ন করতে হবে। আপনাকে [Node.js](https://nodejs.org) ইনস্টল করতে হবে এবং [সার্ভার API চালাতে হবে](../api/README.md) লোকালভাবে যাতে আপনি অ্যাকাউন্ট ডেটা পরিচালনা করতে পারেন।
এই পাঠের জন্য আপনাকে [ডেটা ফেচিং](../3-data/README.md) অংশটি সম্পন্ন করতে হবে। আপনাকে [Node.js](https://nodejs.org) ইনস্টল করতে হবে এবং [সার্ভার API চালাতে হবে](../api/README.md) যাতে আপনি অ্যাকাউন্ট ডেটা পরিচালনা করতে পারেন।
আপনি টার্মিনালে এই কমান্ডটি চালিয়ে নিশ্চিত করতে পারেন যে সার্ভার সঠিকভাবে চলছে:
আপনি টার্মিনালে এই কমান্ডটি চালিয়ে পরীক্ষা করতে পারেন যে সার্ভারটি সঠিকভাবে চলছে কিনা:
```sh
curl http://localhost:5000/api
@ -32,32 +32,32 @@ curl http://localhost:5000/api
---
## স্টেট ম্যানেজমেন্ট পুনর্বিবেচনা করুন
## স্টেট ম্যানেজমেন্ট পুনর্বিবেচনা
[পূর্ববর্তী পাঠে](../3-data/README.md), আমরা আমাদের অ্যাপে একটি মৌলিক স্টেট ধারণা প্রবর্তন করেছি `account` গ্লোবাল ভেরিয়েবল দিয়ে, যা বর্তমানে লগ ইন করা ব্যবহারকারীর ব্যাংক ডেটা ধারণ করে। তবে, আমাদের বর্তমান বাস্তবায়নে কিছু ত্রুটি রয়েছে। ড্যাশবোর্ডে থাকাকালীন পৃষ্ঠা রিফ্রেশ করার চেষ্টা করুন। কী ঘটে?
[পূর্ববর্তী পাঠে](../3-data/README.md), আমরা আমাদের অ্যাপে একটি মৌলিক স্টেট ধারণা প্রবর্তন করেছি `account` গ্লোবাল ভেরিয়েবলের মাধ্যমে, যা বর্তমানে লগ ইন করা ব্যবহারকারীর ব্যাংক ডেটা ধারণ করে। তবে, আমাদের বর্তমান বাস্তবায়নে কিছু ত্রুটি রয়েছে। ড্যাশবোর্ডে থাকাকালীন পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন। কী ঘটে?
বর্তমান কোডে তিনটি সমস্যা রয়েছে:
বর্তমান কোডে ৩টি সমস্যা রয়েছে:
- স্টেট সংরক্ষিত নয়, কারণ ব্রাউজার রিফ্রেশ আপনাকে লগইন পৃষ্ঠায় ফিরিয়ে নিয়ে যায়।
- স্টেট পরিবর্তন করার জন্য একাধিক ফাংশন রয়েছে। অ্যাপটি বড় হলে এটি পরিবর্তনগুলো ট্র্যাক করা কঠিন করে তুলতে পারে এবং একটি আপডেট করতে ভুল হওয়া সহজ।
- স্টেট পরিষ্কার করা হয় না, তাই যখন আপনি *লগআউট*ক্লিক করেন তখন অ্যাকাউন্ট ডেটা এখনও থাকে যদিও আপনি লগইন পৃষ্ঠায় রয়েছেন।
- স্টেট সংরক্ষিত হয় না, কারণ ব্রাউজার রিফ্রেশ আপনাকে লগইন পৃষ্ঠায় ফিরিয়ে নিয়ে যায়।
- স্টেট পরিবর্তন করে এমন একাধিক ফাংশন রয়েছে। অ্যাপটি বড় হলে এটি পরিবর্তনগুলি ট্র্যাক করা কঠিন করে তোলে এবং একটি আপডেট ভুলে যাওয়া সহজ।
- স্টেট পরিষ্কার করা হয় না, তাই যখন আপনি *লগআউট*এ ক্লিক করেন, তখনও অ্যাকাউন্ট ডেটা থেকে যায় যদিও আপনি লগইন পৃষ্ঠায় আছেন।
আমরা এই সমস্যাগুলো একে একে সমাধান করতে আমাদের কোড আপডেট করতে পারি, তবে এটি আরও কোড ডুপ্লিকেশন তৈরি করবে এবং অ্যাপটিকে আরও জটিল এবং রক্ষণাবেক্ষণ করা কঠিন করে তুলবে। অথবা আমরা কয়েক মিনিটের জন্য বিরতি নিতে পারি এবং আমাদের কৌশল পুনর্বিবেচনা করতে পারি।
আমরা এই সমস্যাগুলি একে একে সমাধান করতে আমাদের কোড আপডেট করতে পারি, তবে এটি আরও কোড ডুপ্লিকেশন তৈরি করবে এবং অ্যাপটিকে আরও জটিল এবং বজায় রাখা কঠিন করে তুলবে। অথবা আমরা কয়েক মিনিটের জন্য বিরতি নিয়ে আমাদের কৌশল পুনর্বিবেচনা করতে পারি।
> আমরা এখানে আসলে কোন সমস্যাগুলো সমাধান করার চেষ্টা করছি?
> এখানে আমরা আসলে কোন সমস্যাগুলি সমাধান করার চেষ্টা করছি?
[স্টেট ম্যানেজমেন্ট](https://en.wikipedia.org/wiki/State_management) হলো এই দুটি নির্দিষ্ট সমস্যার জন্য একটি ভালো পদ্ধতি খুঁজে বের করার বিষয়ে:
[স্টেট ম্যানেজমেন্ট](https://en.wikipedia.org/wiki/State_management) হল এই দুটি নির্দিষ্ট সমস্যার জন্য একটি ভাল পদ্ধতি খুঁজে বের করা:
- কীভাবে একটি অ্যাপে ডেটা প্রবাহগুলোকে বোধগম্য রাখা যায়?
- কীভাবে স্টেট ডেটাকে সবসময় ব্যবহারকারীর ইন্টারফেসের সাথে সিঙ্কে রাখা যায় (এবং এর বিপরীত)?
- কীভাবে একটি অ্যাপে ডেটা প্রবাহগুলো বোধগম্য রাখা যায়?
- কীভাবে স্টেট ডেটা সর্বদা ব্যবহারকারীর ইন্টারফেসের সাথে সিঙ্কে রাখা যায় (এবং এর বিপরীত)?
আপনি যখন এগুলো যত্ন সহকারে পরিচালনা করবেন, তখন আপনার যে কোনো অন্যান্য সমস্যা হয়তো ইতিমধ্যেই সমাধান হয়ে যাবে বা সমাধান করা সহজ হয়ে যাবে। এই সমস্যাগুলো সমাধানের জন্য অনেক সম্ভাব্য পদ্ধতি রয়েছে, তবে আমরা একটি সাধারণ সমাধান গ্রহণ করব যা **ডেটা এবং এটি পরিবর্তনের উপায়গুলো কেন্দ্রীভূত করার** ধারণা নিয়ে কাজ করে। ডেটা প্রবাহগুলো এইভাবে চলবে:
আপনি যখন এগুলির যত্ন নেবেন, তখন আপনার যে কোনো অন্যান্য সমস্যা হয়তো ইতিমধ্যেই সমাধান হয়ে যাবে বা সমাধান করা সহজ হয়ে যাবে। এই সমস্যাগুলি সমাধানের জন্য অনেক সম্ভাব্য পদ্ধতি রয়েছে, তবে আমরা একটি সাধারণ সমাধান বেছে নেব যা **ডেটা এবং এটি পরিবর্তনের উপায়গুলিকে কেন্দ্রীভূত করার** ধারণার উপর ভিত্তি করে। ডেটা প্রবাহগুলো নিম্নরূপ হবে:


> আমরা এখানে সেই অংশটি কভার করব না যেখানে ডেটা স্বয়ংক্রিয়ভাবে ভিউ আপডেটকে ট্রিগার করে, কারণ এটি [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming)-এর আরও উন্নত ধারণার সাথে সম্পর্কিত। এটি একটি গভীর ডাইভের জন্য একটি ভালো অনুসরণ বিষয়।
> এখানে আমরা সেই অংশটি কভার করব না যেখানে ডেটা স্বয়ংক্রিয়ভাবে ভিউ আপডেটকে ট্রিগার করে, কারণ এটি [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming)-এর আরও উন্নত ধারণার সাথে সম্পর্কিত। এটি একটি গভীর ডাইভের জন্য একটি ভাল অনুসরণ বিষয়।
✅ স্টেট ম্যানেজমেন্টের জন্য অনেক লাইব্রেরি রয়েছে, [Redux](https://redux.js.org) একটি জনপ্রিয় অপশন। এর ধারণা এবং প্যাটার্নগুলো দেখুন কারণ এটি প্রায়ই বড় ওয়েব অ্যাপগুলোতে সম্ভাব্য সমস্যাগুলো এবং কীভাবে সেগুলো সমাধান করা যায় তা শেখার একটি ভালো উপায়।
✅ স্টেট ম্যানেজমেন্টের জন্য বিভিন্ন পদ্ধতির সাথে অনেক লাইব্রেরি রয়েছে, [Redux](https://redux.js.org) একটি জনপ্রিয় বিকল্প। এর ধারণা এবং প্যাটার্নগুলি দেখুন কারণ এটি প্রায়শই শেখার একটি ভাল উপায় যে বড় ওয়েব অ্যাপগুলিতে আপনি কী সম্ভাব্য সমস্যার সম্মুখীন হতে পারেন এবং কীভাবে এটি সমাধান করা যায়।
### কাজ
@ -75,9 +75,9 @@ let state = {
};
```
ধারণাটি হলো আমাদের অ্যাপের সমস্ত ডেটাকে একটি একক স্টেট অবজেক্টে *কেন্দ্রীভূত* করা। আমাদের স্টেটে এখন শুধুমাত্র `account` রয়েছে, তাই এটি খুব বেশি পরিবর্তন করে না, তবে এটি ভবিষ্যতের জন্য একটি পথ তৈরি করে।
ধারণাটি হল আমাদের অ্যাপের সমস্ত ডেটা একটি একক স্টেট অবজেক্টে *কেন্দ্রীভূত* করা। আপাতত আমাদের স্টেটে শুধুমাত্র `account` রয়েছে, তাই এটি খুব বেশি পরিবর্তন করে না, তবে এটি ভবিষ্যতের জন্য একটি পথ তৈরি করে।
আমাদের এটি ব্যবহারকারী ফাংশনগুলোও আপডেট করতে হবে। `register()` এবং `login()` ফাংশনে, `account = ...` প্রতিস্থাপন করুন `state.account = ...`;
আমাদের এটি ব্যবহারকারী ফাংশনগুলিও আপডেট করতে হবে। `register()` এবং `login()` ফাংশনে, `account = ...` প্রতিস্থাপন করুন `state.account = ...`;
`updateDashboard()` ফাংশনের শুরুতে এই লাইনটি যোগ করুন:
@ -89,13 +89,13 @@ const account = state.account;
## ডেটা পরিবর্তন ট্র্যাক করুন
এখন আমরা আমাদের ডেটা সংরক্ষণ করার জন্য`state` অবজেক্টটি স্থাপন করেছি, পরবর্তী ধাপ হলো আপডেটগুলো কেন্দ্রীভূত করা। লক্ষ্য হলো যে কোনো পরিবর্তন এবং কখন তা ঘটে তা ট্র্যাক করা সহজ করা।
এখন যেহেতু আমরা আমাদের ডেটা সংরক্ষণ করতে`state` অবজেক্টটি স্থাপন করেছি, পরবর্তী ধাপ হল আপডেটগুলো কেন্দ্রীভূত করা। লক্ষ্য হল যে কোনো পরিবর্তন এবং কখন সেগুলি ঘটে তা ট্র্যাক করা সহজ করা।
`state` অবজেক্টে পরিবর্তনগুলো এড়াতে, এটিকে [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) বিবেচনা করাও একটি ভালো অভ্যাস, যার অর্থ এটি মোটেও পরিবর্তন করা যাবে না। এর অর্থ হলো আপনি যদি এতে কিছু পরিবর্তন করতে চান তবে আপনাকে একটি নতুন স্টেট অবজেক্ট তৈরি করতে হবে। এটি করে আপনি সম্ভাব্য অনাকাঙ্ক্ষিত [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) সম্পর্কে সুরক্ষা তৈরি করেন এবং আপনার অ্যাপে নতুন বৈশিষ্ট্য বাস্তবায়নের জন্য সম্ভাবনা খুলে দেন যেমন undo/redo বাস্তবায়ন করা, পাশাপাশি এটি ডিবাগ করা সহজ করে তোলে। উদাহরণস্বরূপ, আপনি স্টেটে করা প্রতিটি পরিবর্তন লগ করতে পারেন এবং একটি বাগের উৎস বুঝতে পরিবর্তনের ইতিহাস রাখতে পারেন।
`state` অবজেক্টে পরিবর্তনগুলি এড়াতে, এটিকে [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) বিবেচনা করাও একটি ভাল অভ্যাস, যার অর্থ এটি মোটেও পরিবর্তন করা যাবে না। এর অর্থ হল আপনি যদি এতে কিছু পরিবর্তন করতে চান তবে আপনাকে একটি নতুন স্টেট অবজেক্ট তৈরি করতে হবে। এটি করে, আপনি সম্ভাব্য অবাঞ্ছিত [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) সম্পর্কে সুরক্ষা তৈরি করেন এবং আপনার অ্যাপে নতুন বৈশিষ্ট্য বাস্তবায়নের সম্ভাবনা খুলে দেন যেমন undo/redo বাস্তবায়ন করা, পাশাপাশি এটি ডিবাগ করাও সহজ করে তোলে। উদাহরণস্বরূপ, আপনি স্টেটে করা প্রতিটি পরিবর্তন লগ করতে এবং একটি বাগের উত্স বুঝতে পরিবর্তনের একটি ইতিহাস রাখতে পারেন।
জাভাস্ক্রিপ্টে, আপনি [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) ব্যবহার করতে পারেন একটি অবজেক্টের অপরিবর্তনীয় সংস্করণ তৈরি করতে। আপনি যদি একটি অপরিবর্তনীয় অবজেক্টে পরিবর্তন করার চেষ্টা করেন, তবে একটি ব্যতিক্রম উত্থাপিত হবে।
জাভাস্ক্রিপ্টে, আপনি [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) ব্যবহার করে একটি অবিচলিত সংস্করণ তৈরি করতে পারেন। যদি আপনি একটি অবিচলিত অবজেক্টে পরিবর্তন করার চেষ্টা করেন, তবে একটি ব্যতিক্রম উত্থাপিত হবে।
✅ আপনি কি জানেন একটি *shallow* এবং একটি *deep* অপরিবর্তনীয় অবজেক্টের মধ্যে পার্থক্য কী? আপনি এটি সম্পর্কে [এখানে](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) পড়তে পারেন।
✅ আপনি কি জানেন একটি *shallow* এবং একটি *deep* অবিচলিত অবজেক্টের মধ্যে পার্থক্য কী? আপনি এটি সম্পর্কে পড়তে পারেন [এখানে](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)।
### কাজ
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
এই ফাংশনে, আমরা একটি নতুন স্টেট অবজেক্ট তৈরি করছি এবং পূর্ববর্তী স্টেট থেকে ডেটা কপি করছি [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) ব্যবহার করে। তারপর আমরা একটি নির্দিষ্ট প্রপার্টি স্টেট অবজেক্টে নতুন ডেটা দিয়ে ওভাররাইড করি [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` ব্যবহার করে। অবশেষে, আমরা `Object.freeze()` ব্যবহার করে অবজেক্টটি লক করি যাতে পরিবর্তনগুলো প্রতিরোধ করা যায়। আমাদের স্টেটে বর্তমানে শুধুমাত্র `account` প্রপার্টি সংরক্ষিত আছে, তবে এই পদ্ধতির মাধ্যমে আপনি স্টেটে যত প্রপার্টি প্রয়োজন যোগ করতে পারেন।
এই ফাংশনে, আমরা একটি নতুন স্টেট অবজেক্ট তৈরি করছি এবং পূর্ববর্তী স্টেট থেকে ডেটা কপি করছি [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) ব্যবহার করে। তারপর আমরা স্টেট অবজেক্টের একটি নির্দিষ্ট প্রপার্টি নতুন ডেটা দিয়ে ওভাররাইট করছি [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` ব্যবহার করে। অবশেষে, আমরা `Object.freeze()` ব্যবহার করে অবজেক্টটি লক করি যাতে পরিবর্তন প্রতিরোধ করা যায়। আপাতত আমাদের স্টেটে শুধুমাত্র `account` প্রপার্টি সংরক্ষিত আছে, তবে এই পদ্ধতির সাথে আপনি স্টেটে যত প্রপার্টি প্রয়োজন যোগ করতে পারেন।
আমরা নিশ্চিত করতে`state` ইনিশিয়ালাইজেশনও আপডেট করব যাতে প্রাথমিক স্টেটও ফ্রিজ করা হয়:
আমরা নিশ্চিত করব যে প্রাথমিক স্টেটও ফ্রোজেন, এজন্য`state` ইনিশিয়ালাইজেশন আপডেট করব:
একটি নতুন অ্যাকাউন্ট রেজিস্টার করার চেষ্টা করুন, লগআউট করুন এবং আবার লগইন করুন যাতে সবকিছু এখনও সঠিকভাবে কাজ করছে কিনা তা পরীক্ষা করা যায়।
একটি নতুন অ্যাকাউন্ট রেজিস্টার করুন, লগআউট করুন এবং আবার লগইন করুন এটি সঠিকভাবে কাজ করছে কিনা তা পরীক্ষা করতে।
> টিপ: আপনি ব্রাউজারের ডেভেলপমেন্ট টুলসের কনসোলে `updateState()`-এর নিচে `console.log(state)` যোগ করে সমস্ত স্টেট পরিবর্তন দেখতে পারেন।
> টিপ: আপনি ব্রাউজারের ডেভেলপমেন্ট টুলসের কনসোলে `updateState()`-এর শেষে `console.log(state)` যোগ করে সমস্ত স্টেট পরিবর্তন দেখতে পারেন।
## স্টেট সংরক্ষণ করুন
বেশিরভাগ ওয়েব অ্যাপ সঠিকভাবে কাজ করতে ডেটা সংরক্ষণ করতে হয়। সমস্ত গুরুত্বপূর্ণ ডেটা সাধারণত একটি ডাটাবেসে সংরক্ষণ করা হয় এবং একটি সার্ভার API এর মাধ্যমে অ্যাক্সেস করা হয়, যেমন আমাদের ক্ষেত্রে ব্যবহারকারীর অ্যাকাউন্ট ডেটা। তবে কখনও কখনও, ব্রাউজারে চলমান ক্লায়েন্ট অ্যাপে কিছু ডেটা সংরক্ষণ করাও আকর্ষণীয় হতে পারে, একটি ভালো ব্যবহারকারীর অভিজ্ঞতা বা লোডিং পারফরম্যান্স উন্নত করার জন্য।
অধিকাংশ ওয়েব অ্যাপ্লিকেশনের সঠিকভাবে কাজ করার জন্য ডেটা সংরক্ষণ করতে হয়। সমস্ত গুরুত্বপূর্ণ ডেটা সাধারণত একটি ডাটাবেসে সংরক্ষণ করা হয় এবং একটি সার্ভার API এর মাধ্যমে অ্যাক্সেস করা হয়, যেমন আমাদের ক্ষেত্রে ব্যবহারকারীর অ্যাকাউন্ট ডেটা। তবে কখনও কখনও, ব্রাউজারে চলমান ক্লায়েন্ট অ্যাপে কিছু ডেটা সংরক্ষণ করাও আকর্ষণীয়, একটি ভাল ব্যবহারকারীর অভিজ্ঞতার জন্য বা লোডিং পারফরম্যান্স উন্নত করার জন্য।
যখন আপনি আপনার ব্রাউজারে ডেটা সংরক্ষণ করতে চান, তখন কয়েকটি গুরুত্বপূর্ণ প্রশ্ন জিজ্ঞাসা করা উচিত:
যখন আপনি আপনার ব্রাউজারে ডেটা সংরক্ষণ করতে চান, তখন আপনাকে কয়েকটি গুরুত্বপূর্ণ প্রশ্ন জিজ্ঞাসা করা উচিত:
- *ডেটা কি সংবেদনশীল?* আপনি ক্লায়েন্টে কোনো সংবেদনশীল ডেটা সংরক্ষণ এড়িয়ে চলা উচিত, যেমন ব্যবহারকারীর পাসওয়ার্ড।
- *আপনার এই ডেটা কতক্ষণ ধরে রাখতে হবে?* আপনি কি এই ডেটা শুধুমাত্র বর্তমান সেশনের জন্য অ্যাক্সেস করতে চান নাকি এটি চিরতরে সংরক্ষণ করতে চান?
- *আপনাকে কতক্ষণ এই ডেটা রাখতে হবে?* আপনি কি শুধুমাত্র বর্তমান সেশনের জন্য এই ডেটা অ্যাক্সেস করার পরিকল্পনা করছেন নাকি এটি চিরতরে সংরক্ষণ করতে চান?
ওয়েব অ্যাপের ভিতরে তথ্য সংরক্ষণের জন্য বিভিন্ন উপায় রয়েছে, আপনি যা অর্জন করতে চান তার উপর নির্ভর করে। উদাহরণস্বরূপ, আপনি একটি সার্চ কোয়েরি সংরক্ষণ করতে URL ব্যবহার করতে পারেন এবং এটি ব্যবহারকারীদের মধ্যে শেয়ারযোগ্য করতে পারেন। আপনি [HTTP কুকিজ](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ব্যবহার করতে পারেন যদি ডেটা সার্ভারের সাথে শেয়ার করতে হয়, যেমন [authentication](https://en.wikipedia.org/wiki/Authentication) তথ্য।
ওয়েব অ্যাপ্লিকেশনের ভিতরে তথ্য সংরক্ষণের জন্য বিভিন্ন উপায় রয়েছে, আপনি যা অর্জন করতে চান তার উপর নির্ভর করে। উদাহরণস্বরূপ, আপনি একটি সার্চ কোয়েরি সংরক্ষণ করতে URL ব্যবহার করতে পারেন এবং এটি ব্যবহারকারীদের মধ্যে শেয়ারযোগ্য করতে পারেন। আপনি [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ব্যবহার করতে পারেন যদি ডেটা সার্ভারের সাথে শেয়ার করতে হয়, যেমন [authentication](https://en.wikipedia.org/wiki/Authentication) তথ্য।
আরেকটি অপশন হলো ডেটা সংরক্ষণের জন্য ব্রাউজারের অনেক API এর মধ্যে একটি ব্যবহার করা। দুটি বিশেষভাবে আকর্ষণীয়:
আরেকটি বিকল্প হল ডেটা সংরক্ষণের জন্য ব্রাউজারের অনেক API-এর একটি ব্যবহার করা। দুটি বিশেষভাবে আকর্ষণীয়:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): একটি [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) যা বিভিন্ন সেশনের মধ্যে বর্তমান ওয়েব সাইটের জন্য নির্দিষ্ট ডেটা সংরক্ষণ করতে দেয়। এতে সংরক্ষিত ডেটা কখনোই মেয়াদোত্তীর্ণ হয় না।
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): এটি `localStorage` এর মতো কাজ করে তবে এতে সংরক্ষিত ডেটা সেশন শেষ হলে (যখন ব্রাউজার বন্ধ হয়) মুছে ফেলা হয়।
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): একটি [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) যা বিভিন্ন সেশনের মধ্যে নির্দিষ্ট ওয়েব সাইটের ডেটা সংরক্ষণ করতে দেয়। এতে সংরক্ষিত ডেটা কখনও মেয়াদোত্তীর্ণ হয় না।
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): এটি `localStorage`-এর মতোই কাজ করে তবে এতে সংরক্ষিত ডেটা সেশনের শেষে (যখন ব্রাউজার বন্ধ হয়) মুছে ফেলা হয়।
উল্লেখ্য যে এই দুটি API শুধুমাত্র [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) সংরক্ষণ করতে দেয়। আপনি যদি জটিল অবজেক্ট সংরক্ষণ করতে চান, তবে আপনাকে এটি [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) ফরম্যাটে সিরিয়ালাইজ করতে হবে [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) ব্যবহার করে।
✅ আপনি যদি একটি ওয়েব অ্যাপ তৈরি করতে চান যা সার্ভার ছাড়াই কাজ করে, তবে এটি [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) ব্যবহার করে ক্লায়েন্টে একটি ডাটাবেস তৈরি করাও সম্ভব। এটি উন্নত ব্যবহারের ক্ষেত্রে বা যদি আপনি উল্লেখযোগ্য পরিমাণ ডেটা সংরক্ষণ করতে চান তবে সংরক্ষিত, কারণ এটি ব্যবহার করা আরও জটিল।
✅ আপনি যদি এমন একটি ওয়েব অ্যাপ তৈরি করতে চান যা সার্ভার ছাড়াই কাজ করে, তবে [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) ব্যবহার করে ক্লায়েন্টে একটি ডাটাবেস তৈরি করাও সম্ভব। এটি উন্নত ব্যবহারের ক্ষেত্রে বা যদি আপনাকে উল্লেখযোগ্য পরিমাণ ডেটা সংরক্ষণ করতে হয় তবে এটি আরও জটিল ব্যবহারের জন্য সংরক্ষিত।
### কাজ
আমরা চাই আমাদের ব্যবহারকারীরা *লগআউট* বোতামে স্পষ্টভাবে ক্লিক না করা পর্যন্ত লগ ইন অবস্থায় থাকুক, তাই আমরা `localStorage` ব্যবহার করব অ্যাকাউন্ট ডেটা সংরক্ষণ করতে। প্রথমে, আসুন একটি কী সংজ্ঞায়িত করি যা আমরা আমাদের ডেটা সংরক্ষণের জন্য ব্যবহার করব।
আমরা চাই আমাদের ব্যবহারকারীরা *লগআউট* বাটনে স্পষ্টভাবে ক্লিক না করা পর্যন্ত লগ ইন থাকুক, তাই আমরা `localStorage` ব্যবহার করে অ্যাকাউন্ট ডেটা সংরক্ষণ করব। প্রথমে, আমরা একটি কী সংজ্ঞায়িত করব যা আমরা আমাদের ডেটা সংরক্ষণ করতে ব্যবহার করব।
এর মাধ্যমে, ব্যবহারকারীর অ্যাকাউন্ট ডেটা সংরক্ষিত থাকবে এবং আমরা পূর্বে কেন্দ্রীভূত সমস্ত স্টেট আপডেটের জন্য সর্বদা আপ-টু-ডেট থাকবে। এটি হলো যেখানে আমরা আমাদের পূর্ববর্তী রিফ্যাক্টরগুলো থেকে উপকৃত হতে শুরু করি 🙂।
এটির মাধ্যমে, ব্যবহারকারীর অ্যাকাউন্ট ডেটা সংরক্ষিত থাকবে এবং পূর্বে কেন্দ্রীভূত করা সমস্ত স্টেট আপডেটের মতো সর্বদা আপডেট থাকবে। এটি সেই জায়গা যেখানে আমরা আমাদের পূর্ববর্তী রিফ্যাক্টরগুলির সমস্ত সুবিধা পেতে শুরু করি 🙂।
ডেটা সংরক্ষিত হওয়ার কারণে, আমাদের অ্যাপ লোড হওয়ার সময় এটি পুনরুদ্ধার করার যত্নও নিতে হবে। যেহেতু আমরা আরও ইনিশিয়ালাইজেশন কোড পেতে শুরু করব, এটি একটি নতুন `init` ফাংশন তৈরি করার জন্য একটি ভালো ধারণা হতে পারে, যা `app.js`-এর নিচে আমাদের পূর্ববর্তী কোডও অন্তর্ভুক্ত করে:
ডেটা সংরক্ষণ করা হয়েছে, তাই আমাদের অ্যাপটি লোড হওয়ার সময় এটি পুনরুদ্ধার করার যত্নও নিতে হবে। যেহেতু আমরা আরও ইনিশিয়ালাইজেশন কোড পেতে শুরু করব, এটি একটি নতুন `init` ফাংশন তৈরি করার একটি ভাল ধারণা হতে পারে, যা `app.js`-এর নীচের পূর্ববর্তী কোডটিও অন্তর্ভুক্ত করে:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
এখানে আমরা সংরক্ষিত ডেটা পুনরুদ্ধার করি, এবং যদি কোনো ডেটা থাকে তবে আমরা স্টেট অনুযায়ী আপডেট করি। এটি *রুট* আপডেট করার *আগে* করা গুরুত্বপূর্ণ, কারণ পৃষ্ঠা আপডেটের সময় স্টেটের উপর নির্ভরশীল কোড থাকতে পারে।
এখানে আমরা সংরক্ষিত ডেটা পুনরুদ্ধার করি, এবং যদি কোনো ডেটা থাকে তবে আমরা স্টেটটি অনুযায়ী আপডেট করি। এটি রুট আপডেট করার *আগে* করা গুরুত্বপূর্ণ, কারণ পৃষ্ঠা আপডেটের সময় স্টেটের উপর নির্ভরশীল কোড থাকতে পারে।
আমরা *ড্যাশবোর্ড* পৃষ্ঠাকে আমাদের অ্যাপ্লিকেশনের ডিফল্ট পৃষ্ঠা করতে পারি, কারণ আমরা এখন অ্যাকাউন্ট ডেটা সংরক্ষণ করছি। যদি কোনো ডেটা পাওয়া না যায়, ড্যাশবোর্ড যেকোনোভাবে *লগইন* পৃষ্ঠায় রিডিরেক্ট করার যত্ন নেয়। `updateRoute()`-এ, `return navigate('/login');` ফallback প্রতিস্থাপন করুন `return navigate('/dashboard');` দিয়ে।
আমরা *ড্যাশবোর্ড* পৃষ্ঠাটিকে আমাদের অ্যাপ্লিকেশনের ডিফল্ট পৃষ্ঠা বানাতে পারি, কারণ আমরা এখন অ্যাকাউন্ট ডেটা সংরক্ষণ করছি। যদি কোনো ডেটা পাওয়া না যায়, তবে ড্যাশবোর্ডটি যেকোনোভাবে *লগইন* পৃষ্ঠায় রিডিরেক্ট করার যত্ন নেয়। `updateRoute()`-এ, ফallback `return navigate('/login');` প্রতিস্থাপন করুন `return navigate('/dashboard');` দিয়ে।
এখন অ্যাপে লগইন করুন এবং পৃষ্ঠা রিফ্রেশ করার চেষ্টা করুন। আপনি ড্যাশবোর্ডে থাকা উচিত। এই আপডেটের মাধ্যমে আমরা আমাদের সমস্ত প্রাথমিক সমস্যার যত্ন নিয়েছি...
এখন অ্যাপে লগইন করুন এবং পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন। আপনি ড্যাশবোর্ডে থাকা উচিত। এই আপডেটের মাধ্যমে আমরা আমাদের সমস্ত প্রাথমিক সমস্যার যত্ন নিয়েছি...
## ডেটা রিফ্রেশ করুন
...কিন্তু আমরা হয়তো একটি নতুন সমস্যা তৈরি করেছি। ওহ!
`test` অ্যাকাউন্ট ব্যবহার করে ড্যাশবোর্ডে যান, তারপর একটি নতুন লেনদেন তৈরি করতে টার্মিনালে এই কমান্ডটি চালান:
`test` অ্যাকাউন্ট ব্যবহার করে ড্যাশবোর্ডে যান, তারপর একটি নতুন লেনদেন তৈরি করতে একটি টার্মিনালে এই কমান্ডটি চালান:
এখন ব্রাউজারে ড্যাশবোর্ড পৃষ্ঠা রিফ্রেশ করার চেষ্টা করুন। কী ঘটে? আপনি কি নতুন লেনদেনটি দেখতে পাচ্ছেন?
এখন আপনার ব্রাউজারে ড্যাশবোর্ড পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন। কী ঘটে? আপনি কি নতুন লেনদেনটি দেখতে পাচ্ছেন?
স্টেটটি `localStorage` এর মাধ্যমে অনির্দিষ্টকালের জন্য সংরক্ষিত থাকে, তবে এর অর্থ হলো এটি কখনোই আপডেট হয় না যতক্ষণ না আপনি অ্যাপ থেকে লগ আউট করেন এবং আবার লগ ইন করেন!
`localStorage`-এর জন্য ধন্যবাদ, স্টেট অনির্দিষ্টকালের জন্য সংরক্ষিত থাকে, তবে এর অর্থ হল এটি কখনও আপডেট হয় না যতক্ষণ না আপনি অ্যাপ থেকে লগ আউট করেন এবং আবার লগ ইন করেন!
এটি ঠিক করার একটি সম্ভাব্য কৌশল হলো ড্যাশবোর্ড লোড হওয়ার প্রতিবার অ্যাকাউন্ট ডেটা পুনরায় লোড করা, স্থবির ডেটা এড়ানোর জন্য।
এটি ঠিক করার একটি সম্ভাব্য কৌশল হল ড্যাশবোর্ড লোড হওয়ার প্রতিবার অ্যাকাউন্ট ডেটা পুনরায় লোড করা, স্থবির ডেটা এড়াতে।
### কাজ
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
এই মেথডটি পরীক্ষা করে যে আমরা বর্তমানে লগ ইন অবস্থায় আছি, তারপর সার্ভার থেকে অ্যাকাউন্ট ডেটা পুনরায় লোড করে।
এই পদ্ধতিটি পরীক্ষা করে যে আমরা বর্তমানে লগ ইন আছি কিনা তারপর সার্ভার থেকে অ্যাকাউন্ট ডেটা পুনরায় লোড করে।
`refresh` নামে আরেকটি ফাংশন তৈরি করুন:
@ -258,7 +258,7 @@ async function refresh() {
}
```
এটি অ্যাকাউন্ট ডেটা আপডেট করে, তারপর ড্যাশবোর্ড পৃষ্ঠার HTML আপডেট করার যত্ন নেয়। এটি হলো যা আমরা ড্যাশবোর্ড রুট লোড হওয়ার সময় কল করতে চাই। রুট সংজ্ঞা আপডেট করুন:
এটি অ্যাকাউন্ট ডেটা আপডেট করে, তারপর ড্যাশবোর্ড পৃষ্ঠার HTML আপডেট করার যত্ন নেয়। এটি আমরা ড্যাশবোর্ড রুট লোড হওয়ার সময় কল করতে চাই। রুট সংজ্ঞাটি আপডেট করুন:
```js
const routes = {
@ -267,26 +267,28 @@ const routes = {
};
```
এখন ড্যাশবোর্ড রিফ্রেশ করার চেষ্টা করুন, এটি আপডেট করা অ্যাকাউন্ট ডেটা প্রদর্শন করা উচিত।
এখন ড্যাশবোর্ড রিফ্রেশ করার চেষ্টা করুন, এটি আপডেট হওয়া অ্যাকাউন্ট ডেটা প্রদর্শন করা উচিত।
---
## 🚀 চ্যালেঞ্জ
এখন আমরা ড্যাশবোর্ড লোড হওয়ার প্রতিবার অ্যাকাউন্ট ডেটা পুনরায় লোড করি, আপনি কি মনে করেন আমরা এখনও *সমস্ত অ্যাকাউন্ট* ডেটা সংরক্ষণ করতে হবে?
এখন যেহেতু আমরা ড্যাশবোর্ড লোড হওয়ার প্রতিবার অ্যাকাউন্ট ডেটা পুনরায় লোড করি, আপনি কি মনে করেন যে আমরা এখনও *সমস্ত অ্যাকাউন্ট* ডেটা সংরক্ষণ করতে হবে?
একসাথে কাজ করার চেষ্টা করুন`localStorage` থেকে কী সংরক্ষণ এবং লোড করা হয় তা পরিবর্তন করতে যাতে অ্যাপটি কাজ করার জন্য শুধুমাত্র যা একেবারে প্রয়োজন তা অন্তর্ভুক্ত করা হয়।
একসঙ্গে কাজ করে`localStorage` থেকে কী সংরক্ষণ এবং লোড করা হয় তা পরিবর্তন করার চেষ্টা করুন যাতে অ্যাপটি কাজ করার জন্য একেবারে প্রয়োজনীয় জিনিসগুলোই অন্তর্ভুক্ত থাকে।
[লেনদেন যোগ করার ডায়ালগ বাস্তবায়ন করুন](assignment.md)
## অ্যাসাইনমেন্ট
এখানে একটি উদাহরণ ফলাফল রয়েছে যা অ্যাসাইনমেন্ট সম্পন্ন করার পরে দেখা যাবে:
["লেনদেন যোগ করুন" ডায়ালগ বাস্তবায়ন করুন](assignment.md)

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

---
**অস্বীকৃতি**:
এই নথিটি AI অনুবাদ পরিষেবা [Co-op Translator](https://github.com/Azure/co-op-translator) ব্যবহার করে অনুবাদ করা হয়েছে। আমরা যথাসাধ্য সঠিকতা নিশ্চিত করার চেষ্টা করি, তবে অনুগ্রহ করে মনে রাখবেন যে স্বয়ংক্রিয় অনুবাদে ত্রুটি বা অসঙ্গতি থাকতে পারে। মূল ভাষায় থাকা নথিটিকে প্রামাণিক উৎস হিসেবে বিবেচনা করা উচিত। গুরুত্বপূর্ণ তথ্যের জন্য, পেশাদার মানব অনুবাদ সুপারিশ করা হয়। এই অনুবাদ ব্যবহারের ফলে কোনো ভুল বোঝাবুঝি বা ভুল ব্যাখ্যা হলে আমরা দায়বদ্ধ থাকব না।
এই নথিটি AI অনুবাদ পরিষেবা [Co-op Translator](https://github.com/Azure/co-op-translator) ব্যবহার করে অনুবাদ করা হয়েছে। আমরা যথাসাধ্য সঠিকতার জন্য চেষ্টা করি, তবে অনুগ্রহ করে মনে রাখবেন যে স্বয়ংক্রিয় অনুবাদে ত্রুটি বা অসঙ্গতি থাকতে পারে। মূল ভাষায় থাকা নথিটিকে প্রামাণিক উৎস হিসেবে বিবেচনা করা উচিত। গুরুত্বপূর্ণ তথ্যের জন্য, পেশাদার মানব অনুবাদ সুপারিশ করা হয়। এই অনুবাদ ব্যবহারের ফলে কোনো ভুল বোঝাবুঝি বা ভুল ব্যাখ্যা হলে আমরা দায়বদ্ধ থাকব না।
# Construindo um App Bancário Parte 4: Conceitos de Gerenciamento de Estado
# Construir um App Bancário Parte 4: Conceitos de Gerenciamento de Estado
## Quiz Pré-Aula
@ -15,13 +15,13 @@ CO_OP_TRANSLATOR_METADATA:
### Introdução
À medida que uma aplicação web cresce, torna-se um desafio acompanhar todos os fluxos de dados. Qual código obtém os dados, qual página os consome, onde e quando eles precisam ser atualizados... é fácil acabar com um código bagunçado e difícil de manter. Isso é especialmente verdadeiro quando você precisa compartilhar dados entre diferentes páginas do seu app, como os dados do usuário. O conceito de *gerenciamento de estado* sempre existiu em todos os tipos de programas, mas à medida que os apps web continuam crescendo em complexidade, tornou-se um ponto-chave a ser considerado durante o desenvolvimento.
À medida que uma aplicação web cresce, torna-se um desafio acompanhar todos os fluxos de dados. Qual código obtém os dados, qual página os consome, onde e quando eles precisam ser atualizados... é fácil acabar com um código bagunçado e difícil de manter. Isso é especialmente verdadeiro quando você precisa compartilhar dados entre diferentes páginas do seu app, como os dados do usuário. O conceito de *gerenciamento de estado* sempre existiu em todos os tipos de programas, mas à medida que os aplicativos web continuam crescendo em complexidade, tornou-se um ponto-chave a ser considerado durante o desenvolvimento.
Nesta última parte, vamos revisar o app que construímos para repensar como o estado é gerenciado, permitindo suporte para atualização do navegador a qualquer momento e persistindo dados entre sessões do usuário.
### Pré-requisitos
Você precisa ter concluído a parte de [busca de dados](../3-data/README.md) do app web para esta lição. Também é necessário instalar o [Node.js](https://nodejs.org) e [executar a API do servidor](../api/README.md) localmente para gerenciar os dados da conta.
Você precisa ter concluído a parte de [busca de dados](../3-data/README.md) do aplicativo web para esta lição. Também é necessário instalar o [Node.js](https://nodejs.org) e [executar a API do servidor](../api/README.md) localmente para gerenciar os dados da conta.
Você pode testar se o servidor está funcionando corretamente executando este comando em um terminal:
@ -32,21 +32,21 @@ curl http://localhost:5000/api
---
## Repensando o gerenciamento de estado
## Repensar o gerenciamento de estado
Na [lição anterior](../3-data/README.md), introduzimos um conceito básico de estado em nosso app com a variável global `account`, que contém os dados bancários do usuário atualmente logado. No entanto, nossa implementação atual tem algumas falhas. Tente atualizar a página enquanto estiver no painel. O que acontece?
Na [lição anterior](../3-data/README.md), introduzimos um conceito básico de estado em nosso app com a variável global `account`, que contém os dados bancários do usuário atualmente logado. No entanto, nossa implementação atual tem algumas falhas. Tente atualizar a página quando estiver no painel. O que acontece?
Há 3 problemas com o código atual:
- O estado não é persistido, pois uma atualização do navegador leva você de volta à página de login.
- Existem várias funções que modificam o estado. À medida que o app cresce, isso pode dificultar o rastreamento das mudanças e é fácil esquecer de atualizar algo.
- Existem várias funções que modificam o estado. À medida que o app cresce, isso pode dificultar o acompanhamento das mudanças e é fácil esquecer de atualizar algo.
- O estado não é limpo, então quando você clica em *Logout*, os dados da conta ainda estão lá, mesmo que você esteja na página de login.
Poderíamos atualizar nosso código para resolver esses problemas um por um, mas isso criaria mais duplicação de código e tornaria o app mais complexo e difícil de manter. Ou poderíamos parar por alguns minutos e repensar nossa estratégia.
> Quais problemas estamos realmente tentando resolver aqui?
[Gerenciamento de estado](https://en.wikipedia.org/wiki/State_management) trata de encontrar uma boa abordagem para resolver esses dois problemas específicos:
[Gerenciamento de estado](https://en.wikipedia.org/wiki/State_management) trata de encontrar uma abordagem eficaz para resolver esses dois problemas específicos:
- Como manter os fluxos de dados em um app compreensíveis?
- Como manter os dados de estado sempre sincronizados com a interface do usuário (e vice-versa)?
@ -55,9 +55,9 @@ Depois de resolver esses problemas, quaisquer outros problemas que você possa t

> Não abordaremos aqui a parte em que os dados automaticamente acionam a atualização da visualização, pois está ligada a conceitos mais avançados de [Programação Reativa](https://en.wikipedia.org/wiki/Reactive_programming). É um bom assunto para um mergulho mais profundo.
> Não abordaremos aqui a parte em que os dados automaticamente acionam a atualização da visualização, pois está ligada a conceitos mais avançados de [Programação Reativa](https://en.wikipedia.org/wiki/Reactive_programming). É um bom assunto para aprofundar se você estiver interessado.
✅ Existem muitas bibliotecas por aí com diferentes abordagens para gerenciamento de estado, sendo o [Redux](https://redux.js.org) uma opção popular. Dê uma olhada nos conceitos e padrões usados, pois muitas vezes é uma boa maneira de aprender quais problemas potenciais você pode enfrentar em apps web grandes e como eles podem ser resolvidos.
✅ Existem muitas bibliotecas com diferentes abordagens para gerenciamento de estado, sendo o [Redux](https://redux.js.org) uma opção popular. Dê uma olhada nos conceitos e padrões usados, pois geralmente é uma boa maneira de aprender quais problemas potenciais você pode enfrentar em grandes aplicativos web e como eles podem ser resolvidos.
### Tarefa
@ -75,7 +75,7 @@ let state = {
};
```
A ideia é *centralizar* todos os dados do nosso app em um único objeto de estado. Por enquanto, só temos `account` no estado, então isso não muda muito, mas cria um caminho para evoluções.
A ideia é *centralizar* todos os dados do nosso app em um único objeto de estado. Por enquanto, temos apenas `account` no estado, então isso não muda muito, mas cria um caminho para evoluções.
Também precisamos atualizar as funções que o utilizam. Nas funções `register()` e `login()`, substitua `account = ...` por `state.account = ...`;
@ -87,13 +87,13 @@ const account = state.account;
Essa refatoração por si só não trouxe muitas melhorias, mas a ideia era preparar o terreno para as próximas mudanças.
## Rastrear mudanças nos dados
## Acompanhar mudanças nos dados
Agora que colocamos em prática o objeto `state` para armazenar nossos dados, o próximo passo é centralizar as atualizações. O objetivo é facilitar o rastreamento de quaisquer mudanças e quando elas acontecem.
Agora que colocamos em prática o objeto `state` para armazenar nossos dados, o próximo passo é centralizar as atualizações. O objetivo é facilitar o acompanhamento de quaisquer mudanças e quando elas acontecem.
Para evitar que mudanças sejam feitas diretamente no objeto `state`, também é uma boa prática considerá-lo [*imutável*](https://en.wikipedia.org/wiki/Immutable_object), o que significa que ele não pode ser modificado de forma alguma. Isso também significa que você precisa criar um novo objeto de estado se quiser alterar algo nele. Ao fazer isso, você constrói uma proteção contra possíveis [efeitos colaterais](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) indesejados e abre possibilidades para novos recursos no seu app, como implementar desfazer/refazer, além de facilitar a depuração. Por exemplo, você poderia registrar todas as mudanças feitas no estado e manter um histórico das alterações para entender a origem de um bug.
Para evitar que alterações sejam feitas diretamente no objeto `state`, também é uma boa prática considerá-lo [*imutável*](https://en.wikipedia.org/wiki/Immutable_object), o que significa que ele não pode ser modificado de forma alguma. Isso também significa que você precisa criar um novo objeto de estado se quiser alterar algo nele. Ao fazer isso, você constrói uma proteção contra possíveis [efeitos colaterais](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) indesejados e abre possibilidades para novos recursos no seu app, como implementar desfazer/refazer, além de facilitar a depuração. Por exemplo, você poderia registrar todas as mudanças feitas no estado e manter um histórico das alterações para entender a origem de um bug.
Em JavaScript, você pode usar [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) para criar uma versão imutável de um objeto. Se você tentar fazer alterações em um objeto imutável, uma exceção será lançada.
No JavaScript, você pode usar [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) para criar uma versão imutável de um objeto. Se você tentar fazer alterações em um objeto imutável, uma exceção será lançada.
✅ Você sabe a diferença entre um objeto imutável *superficial* e *profundo*? Você pode ler sobre isso [aqui](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
Nesta função, estamos criando um novo objeto de estado e copiando os dados do estado anterior usando o [*operador spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Em seguida, substituímos uma propriedade específica do objeto de estado com os novos dados usando a [notação de colchetes](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para atribuição. Por fim, bloqueamos o objeto para evitar modificações usando `Object.freeze()`. Por enquanto, só temos a propriedade `account` armazenada no estado, mas com essa abordagem você pode adicionar quantas propriedades precisar no estado.
Nesta função, estamos criando um novo objeto de estado e copiando os dados do estado anterior usando o [*operador spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Em seguida, substituímos uma propriedade específica do objeto de estado com os novos dados usando a [notação de colchetes](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para atribuição. Por fim, bloqueamos o objeto para evitar modificações usando `Object.freeze()`. Por enquanto, temos apenas a propriedade `account` armazenada no estado, mas com essa abordagem você pode adicionar quantas propriedades precisar no estado.
Também atualizaremos a inicialização do `state` para garantir que o estado inicial seja congelado:
@ -143,31 +143,31 @@ function logout() {
}
```
Na função `updateDashboard()`, substitua o redirecionamento `return navigate('/login');` por `return logout();`;
Na função `updateDashboard()`, substitua o redirecionamento `return navigate('/login');` por `return logout()`;
Tente registrar uma nova conta, fazer logout e login novamente para verificar se tudo ainda funciona corretamente.
> Dica: você pode observar todas as mudanças de estado adicionando `console.log(state)` no final de `updateState()` e abrindo o console nas ferramentas de desenvolvimento do navegador.
> Dica: você pode acompanhar todas as mudanças de estado adicionando `console.log(state)` no final de `updateState()` e abrindo o console nas ferramentas de desenvolvimento do seu navegador.
## Persistir o estado
A maioria dos apps web precisa persistir dados para funcionar corretamente. Todos os dados críticos geralmente são armazenados em um banco de dados e acessados por meio de uma API de servidor, como os dados da conta do usuário no nosso caso. Mas às vezes, também é interessante persistir alguns dados no app cliente que está sendo executado no navegador, para uma melhor experiência do usuário ou para melhorar o desempenho de carregamento.
A maioria dos aplicativos web precisa persistir dados para funcionar corretamente. Todos os dados críticos geralmente são armazenados em um banco de dados e acessados por meio de uma API de servidor, como os dados da conta do usuário no nosso caso. Mas às vezes, também é interessante persistir alguns dados no aplicativo cliente que está sendo executado no navegador, para uma melhor experiência do usuário ou para melhorar o desempenho de carregamento.
Quando você deseja persistir dados no navegador, há algumas perguntas importantes que você deve se fazer:
- *Os dados são sensíveis?* Você deve evitar armazenar qualquer dado sensível no cliente, como senhas de usuários.
- *Por quanto tempo você precisa manter esses dados?* Você planeja acessar esses dados apenas para a sessão atual ou deseja que eles sejam armazenados para sempre?
Existem várias maneiras de armazenar informações dentro de um app web, dependendo do que você deseja alcançar. Por exemplo, você pode usar as URLs para armazenar uma consulta de pesquisa e torná-la compartilhável entre usuários. Você também pode usar [cookies HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) se os dados precisarem ser compartilhados com o servidor, como informações de [autenticação](https://en.wikipedia.org/wiki/Authentication).
Existem várias maneiras de armazenar informações dentro de um aplicativo web, dependendo do que você deseja alcançar. Por exemplo, você pode usar as URLs para armazenar uma consulta de pesquisa e torná-la compartilhável entre usuários. Você também pode usar [cookies HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) se os dados precisarem ser compartilhados com o servidor, como informações de [autenticação](https://en.wikipedia.org/wiki/Authentication).
Outra opção é usar uma das muitas APIs do navegador para armazenar dados. Duas delas são particularmente interessantes:
Outra opção é usar uma das muitas APIs de navegador para armazenar dados. Duas delas são particularmente interessantes:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): um [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) que permite persistir dados específicos do site atual entre diferentes sessões. Os dados salvos nele nunca expiram.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): um [armazenamento de chave/valor](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) que permite persistir dados específicos do site atual entre diferentes sessões. Os dados salvos nele nunca expiram.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funciona da mesma forma que o `localStorage`, exceto que os dados armazenados nele são apagados quando a sessão termina (quando o navegador é fechado).
Observe que ambas as APIs permitem apenas armazenar [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Se você quiser armazenar objetos complexos, precisará serializá-los no formato [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) usando [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Se você quiser criar um app web que não funcione com um servidor, também é possível criar um banco de dados no cliente usando a API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Esta é reservada para casos de uso avançados ou se você precisar armazenar uma quantidade significativa de dados, pois é mais complexa de usar.
✅ Se você quiser criar um aplicativo web que não funcione com um servidor, também é possível criar um banco de dados no cliente usando a API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Essa opção é reservada para casos de uso avançados ou se você precisar armazenar uma quantidade significativa de dados, pois é mais complexa de usar.
### Tarefa
@ -177,7 +177,7 @@ Queremos que nossos usuários permaneçam logados até que cliquem explicitament
const storageKey = 'savedAccount';
```
Em seguida, adicione esta linha ao final da função `updateState()`:
Em seguida, adicione esta linha no final da função `updateState()`:
Com isso, os dados da conta do usuário serão persistidos e sempre atualizados, já que centralizamos anteriormente todas as atualizações de estado. É aqui que começamos a colher os benefícios de todas as nossas refatorações anteriores 🙂.
Como os dados são salvos, também precisamos cuidar de restaurá-los quando o app for carregado. Como começaremos a ter mais código de inicialização, pode ser uma boa ideia criar uma nova função `init`, que também inclui nosso código anterior no final de `app.js`:
Como os dados estão sendo salvos, também precisamos cuidar de restaurá-los quando o app for carregado. Como começaremos a ter mais código de inicialização, pode ser uma boa ideia criar uma nova função `init`, que também inclui nosso código anterior no final de `app.js`:
```js
function init() {
@ -204,7 +204,7 @@ init();
Aqui, recuperamos os dados salvos e, se houver algum, atualizamos o estado de acordo. É importante fazer isso *antes* de atualizar a rota, pois pode haver código que depende do estado durante a atualização da página.
Também podemos tornar a página *Dashboard* a página padrão do nosso app, já que agora estamos persistindo os dados da conta. Se nenhum dado for encontrado, o painel cuida de redirecionar para a página de *Login* de qualquer forma. Em `updateRoute()`, substitua o fallback `return navigate('/login');` por `return navigate('/dashboard');`.
Também podemos tornar a página *Dashboard* a página padrão da nossa aplicação, já que agora estamos persistindo os dados da conta. Se nenhum dado for encontrado, o painel cuida de redirecionar para a página de *Login* de qualquer forma. Em `updateRoute()`, substitua o fallback `return navigate('/login');` por `return navigate('/dashboard');`.
Agora faça login no app e tente atualizar a página. Você deve permanecer no painel. Com essa atualização, resolvemos todos os nossos problemas iniciais...
Agora tente atualizar a página do painel no navegador. O que acontece? Você vê a nova transação?
Agora tente atualizar sua página de painel no navegador. O que acontece? Você vê a nova transação?
O estado é persistido indefinidamente graças ao `localStorage`, mas isso também significa que ele nunca é atualizado até que você saia do app e entre novamente!
O estado é persistido indefinidamente graças ao `localStorage`, mas isso também significa que ele nunca é atualizado até que você saia do app e faça login novamente!
Uma possível estratégia para corrigir isso é recarregar os dados da conta toda vez que o painel for carregado, para evitar dados desatualizados.
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Este método verifica se estamos atualmente logados e então recarrega os dados da conta do servidor.
Este método verifica se estamos atualmente logados e, em seguida, recarrega os dados da conta do servidor.
Crie outra função chamada `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Esta função atualiza os dados da conta e cuida de atualizar o HTML da página do painel. É o que precisamos chamar quando a rota do painel for carregada. Atualize a definição da rota com:
Esta função atualiza os dados da conta e, em seguida, cuida de atualizar o HTML da página do painel. É o que precisamos chamar quando a rota do painel for carregada. Atualize a definição da rota com:
```js
const routes = {
@ -267,7 +267,7 @@ const routes = {
};
```
Agora tente atualizar o painel. Ele deve exibir os dados da conta atualizados.
Agora tente recarregar o painel, ele deve exibir os dados da conta atualizados.
---
@ -278,10 +278,10 @@ Agora que recarregamos os dados da conta toda vez que o painel é carregado, voc
Tente trabalhar em equipe para alterar o que é salvo e carregado do `localStorage` para incluir apenas o que é absolutamente necessário para o app funcionar.
[Implementar o diálogo "Adicionar transação"](assignment.md)
Aqui está um exemplo do resultado após concluir a tarefa:
@ -291,4 +291,4 @@ Aqui está um exemplo do resultado após concluir a tarefa:
---
**Aviso Legal**:
Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, esteja ciente de que traduções automatizadas podem conter erros ou imprecisões. O documento original em seu idioma nativo deve ser considerado a fonte autoritativa. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações equivocadas decorrentes do uso desta tradução.
Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, esteja ciente de que traduções automáticas podem conter erros ou imprecisões. O documento original em seu idioma nativo deve ser considerado a fonte autoritativa. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações equivocadas decorrentes do uso desta tradução.
# Vytvoření bankovní aplikace, část 4: Koncepty správy stavu
## Kvíz před přednáškou
## Kvíz před lekcí
[Kvíz před přednáškou](https://ff-quizzes.netlify.app/web/quiz/47)
[Kvíz před lekcí](https://ff-quizzes.netlify.app/web/quiz/47)
### Úvod
Jak webová aplikace roste, stává se stále obtížnější sledovat všechny datové toky. Který kód získává data, která stránka je spotřebovává, kde a kdy je třeba je aktualizovat... snadno se dostanete k chaotickému kódu, který je obtížné udržovat. To platí zejména tehdy, když potřebujete sdílet data mezi různými stránkami vaší aplikace, například uživatelská data. Koncept *správy stavu*vždy existoval ve všech typech programů, ale jak webové aplikace stále rostou na složitosti, stává se klíčovým bodem, o kterém je třeba během vývoje přemýšlet.
Jak webová aplikace roste, stává se stále obtížnější sledovat všechny datové toky. Který kód získává data, která stránka je spotřebovává, kde a kdy je třeba je aktualizovat... snadno se dostanete k chaotickému kódu, který je těžké udržovat. To platí zejména tehdy, když potřebujete sdílet data mezi různými stránkami vaší aplikace, například uživatelská data. Koncept *správy stavu*existuje ve všech typech programů, ale jak webové aplikace stále rostou na složitosti, stává se klíčovým bodem, o kterém je třeba během vývoje přemýšlet.
V této poslední části se podíváme na aplikaci, kterou jsme vytvořili, abychom přehodnotili, jak je spravován stav, což umožní podporu obnovení prohlížeče kdykoli a zachování dat mezi uživatelskými relacemi.
### Předpoklady
Pro tuto lekci musíte mít dokončenou část [získávání dat](../3-data/README.md) webové aplikace. Také musíte nainstalovat [Node.js](https://nodejs.org) a [spustit server API](../api/README.md) lokálně, abyste mohli spravovat data účtu.
Musíte mít dokončenou část [získávání dat](../3-data/README.md) webové aplikace pro tuto lekci. Také musíte nainstalovat [Node.js](https://nodejs.org) a [spustit server API](../api/README.md) lokálně, abyste mohli spravovat data účtu.
Můžete otestovat, zda server běží správně, spuštěním tohoto příkazu v terminálu:
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## Přehodnocení správy stavu
V [předchozí lekci](../3-data/README.md) jsme představili základní koncept stavu v naší aplikaci s globální proměnnou`account`, která obsahuje bankovní data aktuálně přihlášeného uživatele. Nicméně naše současná implementace má určité nedostatky. Zkuste obnovit stránku, když jste na dashboardu. Co se stane?
V [předchozí lekci](../3-data/README.md) jsme představili základní koncept stavu v naší aplikaci pomocí globální proměnné`account`, která obsahuje bankovní data aktuálně přihlášeného uživatele. Nicméně naše současná implementace má určité nedostatky. Zkuste obnovit stránku, když jste na dashboardu. Co se stane?
Existují 3 problémy se současným kódem:
- Stav není zachován, protože obnovení prohlížeče vás vrátí na přihlašovací stránku.
- Existuje několik funkcí, které upravují stav. Jak aplikace roste, může být obtížné sledovat změny a snadno zapomenete aktualizovat jednu z nich.
- Existuje několik funkcí, které stav mění. Jak aplikace roste, může být obtížné sledovat změny a snadno se zapomene na aktualizaci jedné z nich.
- Stav není vyčištěn, takže když kliknete na *Odhlásit se*, data účtu tam stále jsou, i když jste na přihlašovací stránce.
Mohli bychom aktualizovat náš kód, abychom tyto problémy řešili jeden po druhém, ale vytvořilo by to více duplicitního kódu a učinilo aplikaci složitější a obtížněji udržovatelnou. Nebo bychom se mohli na pár minut zastavit a přehodnotit naši strategii.
> Jaké problémy se zde vlastně snažíme vyřešit?
[Správa stavu](https://en.wikipedia.org/wiki/State_management) je o nalezení dobrého přístupu k řešení těchto dvou konkrétních problémů:
[Správa stavu](https://en.wikipedia.org/wiki/State_management) je především o nalezení dobrého přístupu k řešení těchto dvou konkrétních problémů:
- Jak udržet datové toky v aplikaci srozumitelné?
- Jak udržet stavová data vždy synchronizovaná s uživatelským rozhraním (a naopak)?
- Jak udržet data stavu vždy synchronizovaná s uživatelským rozhraním (a naopak)?
Jakmile se o tyto problémy postaráte, jakékoli další problémy, které byste mohli mít, mohou být buď již vyřešeny, nebo se staly snadněji řešitelnými. Existuje mnoho možných přístupů k řešení těchto problémů, ale zvolíme běžné řešení, které spočívá v **centralizaci dat a způsobů jejich změny**. Datové toky by vypadaly takto:


> Zde nebudeme pokrývat část, kde data automaticky spouštějí aktualizaci zobrazení, protože je spojena s pokročilejšími koncepty [reaktivního programování](https://en.wikipedia.org/wiki/Reactive_programming). Je to dobré téma pro hlubší studium, pokud máte zájem.
✅ Existuje mnoho knihoven s různými přístupy ke správě stavu, [Redux](https://redux.js.org) je populární volbou. Podívejte se na koncepty a vzory, které používá, protože často poskytují dobrý přehled o potenciálních problémech, kterým můžete čelit ve velkých webových aplikacích, a o tom, jak je lze vyřešit.
✅ Existuje mnoho knihoven s různými přístupy ke správě stavu, [Redux](https://redux.js.org) je populární volbou. Podívejte se na koncepty a vzory, které používá, protože často poskytují dobrý způsob, jak se naučit, jaké potenciální problémy můžete čelit ve velkých webových aplikacích a jak je lze vyřešit.
### Úkol
@ -75,7 +75,7 @@ let state = {
};
```
Myšlenkou je *centralizovat* všechna data naší aplikace do jediného objektu stavu. Zatím máme ve stavu pouze `account`, takže se toho moc nezmění, ale vytváříme cestu pro budoucí rozšíření.
Myšlenkou je *centralizovat* všechna data naší aplikace do jednoho objektu stavu. Zatím máme ve stavu pouze `account`, takže se toho moc nezmění, ale vytváří to cestu pro budoucí rozšíření.
Musíme také aktualizovat funkce, které jej používají. Ve funkcích `register()` a `login()` nahraďte `account = ...` za `state.account = ...`;
@ -89,9 +89,9 @@ Tento refaktoring sám o sobě nepřinesl mnoho zlepšení, ale myšlenkou bylo
## Sledování změn dat
Nyní, když jsme vytvořili objekt `state` pro ukládání našich dat, dalším krokem je centralizace aktualizací. Cílem je usnadnit sledování jakýchkoli změn a kdy k nim dochází.
Nyní, když jsme zavedli objekt `state` pro ukládání našich dat, dalším krokem je centralizace aktualizací. Cílem je usnadnit sledování jakýchkoli změn a kdy k nim dochází.
Aby se zabránilo změnám objektu `state`, je také dobré považovat jej za [*neměnný*](https://en.wikipedia.org/wiki/Immutable_object), což znamená, že jej nelze vůbec upravovat. To také znamená, že musíte vytvořit nový objekt stavu, pokud chcete něco změnit. Tímto způsobem vytváříte ochranu proti potenciálně nežádoucím [vedlejším účinkům](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) a otevíráte možnosti pro nové funkce ve vaší aplikaci, jako je implementace undo/redo, a zároveň usnadňujete ladění. Například byste mohli zaznamenávat každou změnu provedenou ve stavu a uchovávat historii změn, abyste pochopili zdroj chyby.
Aby se zabránilo změnám objektu `state`, je také dobré považovat jej za [*neměnný*](https://en.wikipedia.org/wiki/Immutable_object), což znamená, že jej nelze vůbec upravovat. To také znamená, že musíte vytvořit nový objekt stavu, pokud chcete něco změnit. Tímto způsobem si vytvoříte ochranu proti potenciálně nežádoucím [vedlejším účinkům](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) a otevřete možnosti pro nové funkce ve vaší aplikaci, jako je implementace undo/redo, a zároveň usnadníte ladění. Například byste mohli zaznamenávat každou změnu provedenou ve stavu a uchovávat historii změn, abyste pochopili zdroj chyby.
V JavaScriptu můžete použít [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) k vytvoření neměnné verze objektu. Pokud se pokusíte provést změny neměnného objektu, bude vyvolána výjimka.
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
V této funkci vytváříme nový objekt stavu a kopírujeme data z předchozího stavu pomocí [*operátoru rozbalení (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Poté přepisujeme konkrétní vlastnost objektu stavu novými daty pomocí [notace hranatých závorek](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` pro přiřazení. Nakonec objekt uzamkneme, aby se zabránilo úpravám pomocí `Object.freeze()`. Zatím máme ve stavu pouze vlastnost `account`, ale s tímto přístupem můžete do stavu přidat tolik vlastností, kolik potřebujete.
V této funkci vytváříme nový objekt stavu a kopírujeme data z předchozího stavu pomocí [*operátoru spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Poté přepisujeme konkrétní vlastnost objektu stavu novými daty pomocí [notace hranatých závorek](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` pro přiřazení. Nakonec objekt uzamkneme, aby se zabránilo úpravám pomocí `Object.freeze()`. Zatím máme ve stavu pouze vlastnost `account`, ale s tímto přístupem můžete do stavu přidat tolik vlastností, kolik potřebujete.
Také aktualizujeme inicializaci `state`, abychom zajistili, že počáteční stav bude také zmrazen:
@ -126,7 +126,7 @@ Poté aktualizujte funkci `register` nahrazením přiřazení `state.account = r
updateState('account', result);
```
Uděláme totéž s funkcí `login`, nahrazením `state.account = data;` za:
Udělejte totéž s funkcí `login`, nahrazením `state.account = data;` za:
```js
updateState('account', data);
@ -151,27 +151,27 @@ Zkuste zaregistrovat nový účet, odhlásit se a znovu se přihlásit, abyste o
## Zachování stavu
Většina webových aplikací potřebuje uchovávat data, aby mohla správně fungovat. Všechna kritická data jsou obvykle uložena v databázi a přistupuje se k nim prostřednictvím serverového API, například k datům uživatelského účtu v našem případě. Ale někdy je také zajímavé uchovávat některá data na klientské aplikaci, která běží ve vašem prohlížeči, pro lepší uživatelský zážitek nebo pro zlepšení výkonu načítání.
Většina webových aplikací potřebuje uchovávat data, aby mohla správně fungovat. Všechna kritická data jsou obvykle uložena v databázi a přistupuje se k nim prostřednictvím serverového API, jako jsou například data uživatelského účtu v našem případě. Ale někdy je také zajímavé uchovávat některá data na klientské aplikaci, která běží ve vašem prohlížeči, pro lepší uživatelský zážitek nebo pro zlepšení výkonu načítání.
Když chcete uchovávat data ve svém prohlížeči, existuje několik důležitých otázek, které byste si měli položit:
Když chcete uchovávat data ve vašem prohlížeči, existuje několik důležitých otázek, které byste si měli položit:
- *Jsou data citlivá?* Měli byste se vyhnout ukládání jakýchkoli citlivých dat na klienta, jako jsou hesla uživatelů.
- *Jak dlouho potřebujete tato data uchovávat?* Plánujete přístup k těmto datům pouze pro aktuální relaci, nebo je chcete uchovávat navždy?
- *Jak dlouho potřebujete tato data uchovávat?* Plánujete přistupovat k těmto datům pouze během aktuální relace, nebo je chcete uchovávat navždy?
Existuje několik způsobů, jak ukládat informace uvnitř webové aplikace, v závislosti na tom, čeho chcete dosáhnout. Například můžete použít URL k uložení vyhledávacího dotazu a učinit jej sdílitelným mezi uživateli. Můžete také použít [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), pokud je třeba data sdílet se serverem, například informace o [autentizaci](https://en.wikipedia.org/wiki/Authentication).
Existuje několik způsobů, jak ukládat informace uvnitř webové aplikace, v závislosti na tom, čeho chcete dosáhnout. Například můžete použít URL k uložení vyhledávacího dotazu a učinit jej sdílitelným mezi uživateli. Můžete také použít [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), pokud je potřeba data sdílet se serverem, například informace o [autentizaci](https://en.wikipedia.org/wiki/Authentication).
Další možností je použití jedné z mnoha API prohlížeče pro ukládání dat. Dvě z nich jsou obzvláště zajímavé:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), který umožňuje uchovávat data specifická pro aktuální webovou stránku mezi různými relacemi. Data uložená v něm nikdy nevyprší.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funguje stejně jako `localStorage`, kromě toho, že data uložená v něm jsou vymazána, když relace skončí (když se prohlížeč zavře).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funguje stejně jako `localStorage`, kromě toho, že data uložená v něm jsou vymazána, když relace skončí (když je prohlížeč zavřen).
Všimněte si, že obě tyto API umožňují ukládat pouze [řetězce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Pokud chcete ukládat složité objekty, budete je muset serializovat do formátu [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) pomocí [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Pokud chcete vytvořit webovou aplikaci, která nepracuje se serverem, je také možné vytvořit databázi na klientovi pomocí [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Toto je vyhrazeno pro pokročilé případy použití nebo pokud potřebujete ukládat významné množství dat, protože je složitější na použití.
✅ Pokud chcete vytvořit webovou aplikaci, která nepracuje se serverem, je také možné vytvořit databázi na klientovi pomocí [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Tato možnost je vyhrazena pro pokročilé případy použití nebo pokud potřebujete ukládat významné množství dat, protože je složitější na použití.
### Úkol
Chceme, aby naši uživatelé zůstali přihlášeni, dokud explicitně nekliknou na tlačítko *Odhlásit se*, takže použijeme `localStorage` k ukládání dat účtu. Nejprve definujeme klíč, který použijeme k ukládání našich dat.
Chceme, aby naši uživatelé zůstali přihlášeni, dokud explicitně nekliknou na tlačítko *Odhlásit se*, takže použijeme `localStorage` k ukládání dat účtu. Nejprve definujme klíč, který použijeme k ukládání našich dat.
```js
const storageKey = 'savedAccount';
@ -202,15 +202,15 @@ function init() {
init();
```
Zde získáváme uložená data a pokud nějaká existují, aktualizujeme stav odpovídajícím způsobem. Je důležité to udělat *před* aktualizací trasy, protože během aktualizace stránky může být kód závislý na stavu.
Zde získáváme uložená data a pokud nějaká existují, aktualizujeme stav odpovídajícím způsobem. Je důležité to udělat *před* aktualizací trasy, protože může existovat kód, který se spoléhá na stav během aktualizace stránky.
Můžeme také udělat stránku *Dashboard* výchozí stránkou naší aplikace, protože nyní uchováváme data účtu. Pokud žádná data nejsou nalezena, dashboard se postará o přesměrování na stránku *Login*. V `updateRoute()` nahraďte výchozí `return navigate('/login');` za `return navigate('/dashboard');`.
Nyní se přihlaste do aplikace a zkuste obnovit stránku. Měli byste zůstat na dashboardu. S touto aktualizací jsme se postarali o všechny naše počáteční problémy...
Nyní se přihlaste do aplikace a zkuste obnovit stránku. Měli byste zůstat na dashboardu. Tímto jsme vyřešili všechny naše počáteční problémy...
## Aktualizace dat
...Ale možná jsme také vytvořili nový problém. Ups!
...Ale možná jsme také vytvořili nový problém. Oops!
Přejděte na dashboard pomocí účtu `test`, poté spusťte tento příkaz v terminálu, abyste vytvořili novou transakci:
Zkuste nyní obnovit stránku dashboardu v prohlížeči. Co se stane? Vidíte novou transakci?
Zkuste nyní obnovit stránku dashboardu ve vašem prohlížeči. Co se stane? Vidíte novou transakci?
Stav je uchováván neomezeně díky `localStorage`, ale to také znamená, že se nikdy neaktualizuje, dokud se z aplikace neodhlásíte a znovu nepřihlásíte!
Stav je uchováván neomezeně díky `localStorage`, ale to také znamená, že se nikdy neaktualizuje, dokud se neodhlásíte z aplikace a znovu se nepřihlásíte!
Jednou z možných strategií, jak to opravit, je znovu načíst data účtu pokaždé, když je dashboard načten, aby se zabránilo zastaralým datům.
@ -258,7 +258,7 @@ async function refresh() {
}
```
Tato funkce aktualizuje data účtu a poté se postará o aktualizaci HTML stránky dashboardu. To je to, co potřebujeme zavolat, když je načtena trasa dashboardu. Aktualizujte definici trasy:
Tato funkce aktualizuje data účtu a poté se postará o aktualizaci HTML stránky dashboardu. To je to, co potřebujeme zavolat, když je načtena trasa dashboardu. Aktualizujte definici trasy pomocí:
```js
const routes = {
@ -275,20 +275,20 @@ Zkuste nyní obnovit dashboard, měl by zobrazit aktualizovaná data účtu.
Nyní, když znovu načítáme data účtu pokaždé, když je dashboard načten, myslíte si, že stále potřebujeme uchovávat *všechna data účtu*?
Zkuste společně upravit, co je ukládáno a načítáno z `localStorage`, aby zahrnovalo pouze to, co je absolutně nezbytné pro fungování aplikace.
Zkuste společně změnit, co je ukládáno a načítáno z `localStorage`, aby obsahovalo pouze to, co je absolutně nezbytné pro fungování aplikace.
Tento dokument byl přeložen pomocí služby pro automatický překlad [Co-op Translator](https://github.com/Azure/co-op-translator). I když se snažíme o co největší přesnost, mějte prosím na paměti, že automatické překlady mohou obsahovat chyby nebo nepřesnosti. Původní dokument v jeho původním jazyce by měl být považován za závazný zdroj. Pro důležité informace doporučujeme profesionální lidský překlad. Neodpovídáme za žádná nedorozumění nebo nesprávné výklady vyplývající z použití tohoto překladu.
Tento dokument byl přeložen pomocí služby AI pro překlad [Co-op Translator](https://github.com/Azure/co-op-translator). I když se snažíme o přesnost, mějte prosím na paměti, že automatizované překlady mohou obsahovat chyby nebo nepřesnosti. Původní dokument v jeho původním jazyce by měl být považován za autoritativní zdroj. Pro důležité informace se doporučuje profesionální lidský překlad. Neodpovídáme za žádná nedorozumění nebo nesprávné interpretace vyplývající z použití tohoto překladu.
# Byg en Bankapp Del 4: Koncepter inden for State Management
# Byg en bankapp del 4: Koncepter inden for tilstandsadministration
## Quiz før forelæsning
@ -15,13 +15,13 @@ CO_OP_TRANSLATOR_METADATA:
### Introduktion
Når en webapplikation vokser, bliver det en udfordring at holde styr på alle dataflows. Hvilken kode henter data, hvilken side bruger det, hvor og hvornår skal det opdateres...det er nemt at ende med rodet kode, der er svær at vedligeholde. Dette er især tilfældet, når du skal dele data mellem forskellige sider i din app, for eksempel brugerdata. Konceptet *state management* har altid eksisteret i alle slags programmer, men efterhånden som webapps bliver mere komplekse, er det nu et centralt punkt at tænke over under udviklingen.
Efterhånden som en webapplikation vokser, bliver det en udfordring at holde styr på alle dataflows. Hvilken kode henter data, hvilken side bruger det, hvor og hvornår skal det opdateres...det er nemt at ende med rodet kode, der er svær at vedligeholde. Dette er især tilfældet, når du skal dele data mellem forskellige sider i din app, for eksempel brugerdata. Konceptet *tilstandsadministration* har altid eksisteret i alle slags programmer, men efterhånden som webapps bliver mere komplekse, er det nu et nøglepunkt at tænke over under udviklingen.
I denne sidste del vil vi gennemgå den app, vi har bygget, for at genoverveje, hvordan state håndteres, så vi kan understøtte browseropdatering på ethvert tidspunkt og bevare data på tværs af brugersessioner.
I denne sidste del vil vi gennemgå den app, vi har bygget, for at genoverveje, hvordan tilstanden administreres, så vi kan understøtte browseropdatering på ethvert tidspunkt og bevare data på tværs af brugersessioner.
### Forudsætninger
Du skal have gennemført [datahentning](../3-data/README.md)-delen af webappen for denne lektion. Du skal også installere [Node.js](https://nodejs.org) og [køre server-API'et](../api/README.md) lokalt, så du kan administrere kontodata.
Du skal have gennemført [datahentning](../3-data/README.md)-delen af webappen for denne lektion. Du skal også installere [Node.js](https://nodejs.org) og [køre server-API'en](../api/README.md) lokalt, så du kan administrere kontodata.
Du kan teste, om serveren kører korrekt, ved at udføre denne kommando i en terminal:
@ -32,32 +32,32 @@ curl http://localhost:5000/api
---
## Genovervej state management
## Genovervej tilstandsadministration
I [den forrige lektion](../3-data/README.md) introducerede vi et grundlæggende koncept for state i vores app med den globale `account`-variabel, som indeholder bankdata for den aktuelt loggede bruger. Men vores nuværende implementering har nogle mangler. Prøv at opdatere siden, når du er på dashboardet. Hvad sker der?
I [den forrige lektion](../3-data/README.md) introducerede vi et grundlæggende koncept for tilstand i vores app med den globale `account`-variabel, som indeholder bankdata for den aktuelt loggede bruger. Men vores nuværende implementering har nogle mangler. Prøv at opdatere siden, når du er på dashboardet. Hvad sker der?
Der er tre problemer med den nuværende kode:
- State bliver ikke bevaret, da en browseropdatering sender dig tilbage til login-siden.
- Der er flere funktioner, der ændrer state. Efterhånden som appen vokser, kan det gøre det svært at spore ændringer, og det er nemt at glemme at opdatere en.
- State bliver ikke ryddet op, så når du klikker på *Logout*, er kontodata stadig der, selvom du er på login-siden.
- Tilstanden gemmes ikke, da en browseropdatering sender dig tilbage til login-siden.
- Der er flere funktioner, der ændrer tilstanden. Efterhånden som appen vokser, kan det gøre det svært at spore ændringerne, og det er nemt at glemme at opdatere en.
- Tilstanden ryddes ikke op, så når du klikker på *Log ud*, er kontodataene stadig der, selvom du er på login-siden.
Vi kunne opdatere vores kode for at tackle disse problemer ét ad gangen, men det ville skabe mere kodegentagelse og gøre appen mere kompleks og svær at vedligeholde. Eller vi kunne tage et øjeblik og genoverveje vores strategi.
> Hvilke problemer prøver vi egentlig at løse her?
[State management](https://en.wikipedia.org/wiki/State_management) handler om at finde en god tilgang til at løse disse to specifikke problemer:
[Tilstandsadministration](https://en.wikipedia.org/wiki/State_management) handler om at finde en god tilgang til at løse disse to specifikke problemer:
- Hvordan holder vi dataflows i en app forståelige?
- Hvordan holder vi state-data altid i sync med brugergrænsefladen (og omvendt)?
- Hvordan holder man dataflows i en app forståelige?
- Hvordan holder man tilstandsdata altid synkroniseret med brugergrænsefladen (og omvendt)?
Når du har taget hånd om disse, kan andre problemer enten allerede være løst eller være blevet lettere at løse. Der er mange mulige tilgange til at løse disse problemer, men vi vil vælge en almindelig løsning, der består i **at centralisere data og måderne at ændre dem på**. Dataflows ville se sådan ud:
Når du har taget hånd om disse, kan andre problemer, du måtte have, enten allerede være løst eller være blevet lettere at løse. Der er mange mulige tilgange til at løse disse problemer, men vi vil vælge en almindelig løsning, der består i **at centralisere data og måderne at ændre dem på**. Dataflows ville se sådan ud:


> Vi vil ikke dække den del, hvor data automatisk udløser opdatering af visningen, da det er knyttet til mere avancerede koncepter inden for [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). Det er et godt emne til en dybere dykning.
> Vi vil ikke dække den del, hvor data automatisk udløser visningsopdateringen, da det er knyttet til mere avancerede begreber inden for [reaktiv programmering](https://en.wikipedia.org/wiki/Reactive_programming). Det er et godt emne at dykke ned i, hvis du er klar til en dybere forståelse.
✅ Der findes mange biblioteker med forskellige tilgange til state management, hvor [Redux](https://redux.js.org) er en populær mulighed. Tag et kig på de koncepter og mønstre, der bruges, da det ofte er en god måde at lære om potentielle problemer, du kan stå over for i store webapps, og hvordan de kan løses.
✅ Der findes mange biblioteker med forskellige tilgange til tilstandsadministration, hvor [Redux](https://redux.js.org) er en populær mulighed. Tag et kig på de koncepter og mønstre, der bruges, da det ofte er en god måde at lære, hvilke potentielle problemer du kan stå over for i store webapps, og hvordan de kan løses.
### Opgave
@ -75,7 +75,7 @@ let state = {
};
```
Ideen er at *centralisere* alle vores appdata i et enkelt state-objekt. Vi har kun `account` i state lige nu, så det ændrer ikke meget, men det skaber en vej for fremtidige udvidelser.
Ideen er at *centralisere* alle vores appdata i et enkelt tilstandsobjekt. Vi har kun `account` i tilstanden lige nu, så det ændrer ikke meget, men det skaber en vej for fremtidige udviklinger.
Vi skal også opdatere de funktioner, der bruger det. I funktionerne `register()` og `login()` skal du erstatte `account = ...` med `state.account = ...`;
@ -91,11 +91,11 @@ Denne refaktorering i sig selv har ikke bragt mange forbedringer, men ideen var
Nu hvor vi har oprettet `state`-objektet til at gemme vores data, er næste skridt at centralisere opdateringerne. Målet er at gøre det lettere at holde styr på eventuelle ændringer og hvornår de sker.
For at undgå, at der foretages ændringer i `state`-objektet, er det også en god praksis at betragte det som [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), hvilket betyder, at det slet ikke kan ændres. Det betyder også, at du skal oprette et nyt state-objekt, hvis du vil ændre noget i det. Ved at gøre dette bygger du en beskyttelse mod potentielt uønskede [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) og åbner op for nye funktioner i din app, som f.eks. implementering af undo/redo, samtidig med at det bliver lettere at debugge. For eksempel kunne du logge hver ændring, der foretages i state, og holde en historik over ændringerne for at forstå kilden til en fejl.
For at undgå, at der foretages ændringer i `state`-objektet, er det også en god praksis at betragte det som [*uændret*](https://en.wikipedia.org/wiki/Immutable_object), hvilket betyder, at det slet ikke kan ændres. Det betyder også, at du skal oprette et nyt tilstandsobjekt, hvis du vil ændre noget i det. Ved at gøre dette bygger du en beskyttelse mod potentielt uønskede [sideeffekter](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) og åbner op for muligheder for nye funktioner i din app, såsom implementering af fortryd/gendan, samtidig med at det bliver lettere at fejlfinde. For eksempel kunne du logge hver ændring, der foretages i tilstanden, og holde en historik over ændringerne for at forstå kilden til en fejl.
I JavaScript kan du bruge [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) til at oprette en immutable version af et objekt. Hvis du forsøger at foretage ændringer i et immutable objekt, vil der blive rejst en undtagelse.
I JavaScript kan du bruge [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) til at oprette en uændret version af et objekt. Hvis du forsøger at foretage ændringer i et uændret objekt, vil der blive rejst en undtagelse.
✅ Kender du forskellen mellem et *shallow* og et *deep* immutable objekt? Du kan læse om det [her](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Kender du forskellen mellem et *overfladisk* og et *dybt* uændret objekt? Du kan læse om det [her](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Opgave
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
I denne funktion opretter vi et nyt state-objekt og kopierer data fra det tidligere state ved hjælp af [*spread (`...`) operatoren*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Derefter overskriver vi en bestemt egenskab i state-objektet med de nye data ved hjælp af [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` til tildeling. Til sidst låser vi objektet for at forhindre ændringer ved hjælp af `Object.freeze()`. Vi har kun egenskaben `account` gemt i state lige nu, men med denne tilgang kan du tilføje så mange egenskaber, som du har brug for i state.
I denne funktion opretter vi et nyt tilstandsobjekt og kopierer data fra den tidligere tilstand ved hjælp af [*spread (`...`) operatoren*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Derefter overskriver vi en bestemt egenskab i tilstandsobjektet med de nye data ved hjælp af [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` til tildeling. Til sidst låser vi objektet for at forhindre ændringer ved hjælp af `Object.freeze()`. Vi har kun egenskaben `account` gemt i tilstanden lige nu, men med denne tilgang kan du tilføje så mange egenskaber, som du har brug for i tilstanden.
Vi opdaterer også `state`-initialiseringen for at sikre, at den oprindelige state også er frosset:
Vi opdaterer også `state`-initialiseringen for at sikre, at den oprindelige tilstand også er låst:
```js
let state = Object.freeze({
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
Derefter opdaterer vi `register`-funktionen ved at erstatte `state.account = result;`-tildelingen med:
Derefter opdaterer vi `register`-funktionen ved at erstatte tildelingen `state.account = result;` med:
```js
updateState('account', result);
@ -132,7 +132,7 @@ Gør det samme med `login`-funktionen ved at erstatte `state.account = data;` me
updateState('account', data);
```
Vi benytter nu lejligheden til at løse problemet med, at kontodata ikke bliver ryddet, når brugeren klikker på *Logout*.
Vi vil nu benytte lejligheden til at løse problemet med, at kontodata ikke ryddes, når brugeren klikker på *Log ud*.
Opret en ny funktion `logout()`:
@ -143,47 +143,47 @@ function logout() {
}
```
I `updateDashboard()` skal du erstatte omdirigeringen `return navigate('/login');` med `return logout();`
I `updateDashboard()` skal du erstatte omdirigeringen `return navigate('/login');` med `return logout();`;
Prøv at registrere en ny konto, logge ud og logge ind igen for at kontrollere, at alt stadig fungerer korrekt.
> Tip: Du kan se alle state-ændringer ved at tilføje `console.log(state)` nederst i `updateState()` og åbne konsollen i din browsers udviklingsværktøjer.
> Tip: Du kan se alle tilstandsændringer ved at tilføje `console.log(state)` nederst i `updateState()` og åbne konsollen i din browsers udviklingsværktøjer.
## Bevar state
## Gem tilstanden
De fleste webapps har brug for at bevare data for at kunne fungere korrekt. Alle kritiske data gemmes normalt i en database og tilgås via et server-API, som f.eks. brugerens kontodata i vores tilfælde. Men nogle gange kan det også være interessant at bevare nogle data i klientappen, der kører i din browser, for at give en bedre brugeroplevelse eller forbedre indlæsningshastigheden.
De fleste webapps har brug for at gemme data for at kunne fungere korrekt. Alle kritiske data gemmes normalt i en database og tilgås via en server-API, som brugerens kontodata i vores tilfælde. Men nogle gange er det også interessant at gemme nogle data i klientappen, der kører i din browser, for at give en bedre brugeroplevelse eller forbedre indlæsningsydelsen.
Når du vil bevare data i din browser, er der nogle vigtige spørgsmål, du bør stille dig selv:
Når du vil gemme data i din browser, er der nogle vigtige spørgsmål, du bør stille dig selv:
- *Er dataene følsomme?* Du bør undgå at gemme følsomme data på klienten, såsom brugerens adgangskoder.
- *Hvor længe har du brug for at gemme disse data?* Planlægger du kun at tilgå disse data under den aktuelle session, eller vil du have dem gemt for evigt?
- *Er dataene følsomme?* Du bør undgå at gemme følsomme data på klienten, såsom brugeradgangskoder.
- *Hvor længe har du brug for at gemme disse data?* Planlægger du at tilgå disse data kun for den aktuelle session, eller vil du have dem gemt for evigt?
Der er flere måder at gemme information i en webapp på, afhængigt af hvad du vil opnå. For eksempel kan du bruge URL'er til at gemme en søgeforespørgsel og gøre den delbar mellem brugere. Du kan også bruge [HTTPcookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), hvis dataene skal deles med serveren, som f.eks. [autentifikations](https://en.wikipedia.org/wiki/Authentication)-information.
Der er flere måder at gemme information i en webapp på, afhængigt af hvad du vil opnå. For eksempel kan du bruge URL'er til at gemme en søgeforespørgsel og gøre den delbar mellem brugere. Du kan også bruge [HTTP-cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), hvis dataene skal deles med serveren, såsom [autentifikations](https://en.wikipedia.org/wiki/Authentication)information.
En anden mulighed er at bruge en af de mange browser-API'er til at gemme data. To af dem er særligt interessante:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): en [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), der gør det muligt at bevare data, der er specifikke for det aktuelle websted, på tværs af forskellige sessioner. De gemte data udløber aldrig.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): denne fungerer på samme måde som `localStorage`, bortset fra at de gemte data ryddes, når sessionen slutter (når browseren lukkes).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): en [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), der gør det muligt at gemme data, der er specifikke for det aktuelle websted, på tværs af forskellige sessioner. De data, der gemmes i det, udløber aldrig.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): denne fungerer på samme måde som `localStorage`, bortset fra at de data, der gemmes i det, ryddes, når sessionen afsluttes (når browseren lukkes).
Bemærk, at begge disse API'er kun tillader at gemme [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Hvis du vil gemme komplekse objekter, skal du serialisere dem til [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-formatet ved hjælp af [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Bemærk, at begge disse API'er kun tillader at gemme [strenge](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Hvis du vil gemme komplekse objekter, skal du serialisere dem til [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-formatet ved hjælp af [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Hvis du vil oprette en webapp, der ikke arbejder med en server, er det også muligt at oprette en database på klienten ved hjælp af [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Denne er reserveret til avancerede brugsscenarier eller hvis du har brug for at gemme betydelige mængder data, da den er mere kompleks at bruge.
✅ Hvis du vil oprette en webapp, der ikke fungerer med en server, er det også muligt at oprette en database på klienten ved hjælp af [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Denne er forbeholdt avancerede brugsscenarier eller hvis du har brug for at gemme en betydelig mængde data, da den er mere kompleks at bruge.
### Opgave
Vi vil gerne have, at vores brugere forbliver logget ind, indtil de eksplicit klikker på *Logout*-knappen, så vi bruger`localStorage` til at gemme kontodata. Først definerer vi en nøgle, som vi vil bruge til at gemme vores data.
Vi ønsker, at vores brugere forbliver logget ind, indtil de eksplicit klikker på *Log ud*-knappen, så vi vil bruge`localStorage` til at gemme kontodata. Først skal vi definere en nøgle, som vi vil bruge til at gemme vores data.
```js
const storageKey = 'savedAccount';
```
Tilføj derefter denne linje nederst i`updateState()`-funktionen:
Tilføj derefter denne linje i slutningen af`updateState()`-funktionen:
Med dette vil brugerens kontodata blive bevaret og altid være opdateret, da vi tidligere centraliserede alle vores state-opdateringer. Det er her, vi begynder at drage fordel af alle vores tidligere refaktoreringer 🙂.
Med dette vil brugerens kontodata blive gemt og altid være opdateret, da vi tidligere centraliserede alle vores tilstandsopdateringer. Det er her, vi begynder at drage fordel af alle vores tidligere refaktoreringer 🙂.
Da dataene gemmes, skal vi også tage os af at gendanne dem, når appen indlæses. Da vi begynder at have mere initialiseringskode, kan det være en god idé at oprette en ny `init`-funktion, der også inkluderer vores tidligere kode nederst i `app.js`:
@ -202,15 +202,15 @@ function init() {
init();
```
Her henter vi de gemte data, og hvis der er nogen, opdaterer vi state tilsvarende. Det er vigtigt at gøre dette *før* opdatering af ruten, da der kan være kode, der afhænger af state under sideopdateringen.
Her henter vi de gemte data, og hvis der er nogen, opdaterer vi tilstanden i overensstemmelse hermed. Det er vigtigt at gøre dette *før* opdatering af ruten, da der kan være kode, der afhænger af tilstanden under sideopdateringen.
Vi kan også gøre *Dashboard*-siden til vores applikations standardside, da vi nu bevarer kontodata. Hvis der ikke findes data, sørger dashboardet for at omdirigere til *Login*-siden alligevel. I `updateRoute()` skal du erstatte fallback `return navigate('/login');` med `return navigate('/dashboard');`.
Vi kan også gøre *Dashboard*-siden til vores applikations standardside, da vi nu gemmer kontodataene. Hvis der ikke findes data, sørger dashboardet for at omdirigere til *Login*-siden alligevel. I `updateRoute()` skal du erstatte fallbacken`return navigate('/login');` med `return navigate('/dashboard');`.
Log nu ind i appen og prøv at opdatere siden. Du bør forblive på dashboardet. Med denne opdatering har vi taget hånd om alle vores oprindelige problemer...
## Opdater dataene
...Men vi har måske også skabt et nyt problem. Ups!
...Men vi har måske også skabt et nyt. Ups!
Gå til dashboardet ved hjælp af `test`-kontoen, og kør derefter denne kommando i en terminal for at oprette en ny transaktion:
Prøv at opdatere dashboard-siden i browseren nu. Hvad sker der? Ser du den nye transaktion?
Prøv at opdatere din dashboard-side i browseren nu. Hvad sker der? Ser du den nye transaktion?
State bevares på ubestemt tid takket være `localStorage`, men det betyder også, at det aldrig opdateres, før du logger ud af appen og logger ind igen!
Tilstanden gemmes på ubestemt tid takket være `localStorage`, men det betyder også, at den aldrig opdateres, før du logger ud af appen og logger ind igen!
En mulig strategi for at løse dette er at genindlæse kontodataene hver gang dashboardet indlæses, for at undgå forældede data.
@ -258,7 +258,7 @@ async function refresh() {
}
```
Denne opdaterer kontodataene og sørger derefter for at opdatere HTML'en på dashboard-siden. Det er den, vi skal kalde, når dashboard-ruten indlæses. Opdater rutedefinitionen med:
Denne opdaterer kontodataene og tager sig derefter af at opdatere HTML'en på dashboard-siden. Det er det, vi skal kalde, når dashboard-ruten indlæses. Opdater rutedefinitionen med:
```js
const routes = {
@ -273,22 +273,22 @@ Prøv at genindlæse dashboardet nu, det burde vise de opdaterede kontodata.
## 🚀 Udfordring
Nu hvor vi genindlæser kontodataene hver gang dashboardet indlæses, tror du, vi stadig har brug for at bevare *alle kontodata*?
Nu hvor vi genindlæser kontodataene hver gang dashboardet indlæses, tror du, vi stadig har brug for at gemme *alle kontodata*?
Prøv at arbejde sammen om at ændre, hvad der gemmes og hentes fra `localStorage`, så det kun inkluderer det, der er absolut nødvendigt for, at appen fungerer.
Prøv at arbejde sammen om at ændre, hvad der gemmes og indlæses fra `localStorage`, så det kun inkluderer det, der er absolut nødvendigt for, at appen fungerer.
## Quiz efter forelæsning
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/48)
Her er et eksempel på resultatet efter at have fuldført opgaven:


---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på at sikre nøjagtighed, skal det bemærkes, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os ikke ansvar for eventuelle misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal det bemærkes, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os ikke ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
# Erstellen einer Banking-App Teil 4: Konzepte des State Managements
# Erstellen einer Banking-App Teil 4: Konzepte des Zustandsmanagements
## Quiz vor der Vorlesung
@ -15,13 +15,13 @@ CO_OP_TRANSLATOR_METADATA:
### Einführung
Wenn eine Webanwendung wächst, wird es zunehmend schwierig, alle Datenflüsse im Blick zu behalten. Welcher Code ruft die Daten ab, welche Seite nutzt sie, wo und wann müssen sie aktualisiert werden... Es ist leicht, am Ende mit unübersichtlichem Code dazustehen, der schwer zu warten ist. Dies gilt besonders, wenn Daten zwischen verschiedenen Seiten der App geteilt werden müssen, wie z. B. Benutzerdaten. Das Konzept des *State Managements* existiert schon immer in allen Arten von Programmen, aber da Web-Apps immer komplexer werden, ist es mittlerweile ein zentraler Punkt, über den man während der Entwicklung nachdenken muss.
Mit dem Wachstum einer Webanwendung wird es immer schwieriger, alle Datenflüsse im Blick zu behalten. Welcher Code holt die Daten, welche Seite nutzt sie, wo und wann müssen sie aktualisiert werden... Es ist leicht, unübersichtlichen Code zu erstellen, der schwer zu warten ist. Dies gilt insbesondere, wenn Daten zwischen verschiedenen Seiten der App geteilt werden müssen, wie z. B. Benutzerdaten. Das Konzept des *Zustandsmanagements* existiert schon immer in allen Arten von Programmen, aber mit der zunehmenden Komplexität von Webanwendungen ist es heute ein zentraler Punkt bei der Entwicklung.
In diesem letzten Teil werden wir die App, die wir gebaut haben, überarbeiten, um das State Management neu zu denken. Ziel ist es, die Unterstützung für Browser-Aktualisierungen zu jeder Zeit zu ermöglichen und Daten über Benutzersitzungen hinweg zu speichern.
In diesem letzten Teil werfen wir einen Blick auf die App, die wir erstellt haben, und überdenken, wie der Zustand verwaltet wird, um Unterstützung für Browseraktualisierungen zu jedem Zeitpunkt zu ermöglichen und Daten über Benutzersitzungen hinweg zu speichern.
### Voraussetzungen
Du musst den [Datenabruf](../3-data/README.md) Teil der Web-App abgeschlossen haben, um diese Lektion zu bearbeiten. Außerdem musst du [Node.js](https://nodejs.org) installieren und [den Server-API](../api/README.md) lokal ausführen, damit du Kontodaten verwalten kannst.
Du solltest den [Datenabruf](../3-data/README.md)-Teil der Web-App für diese Lektion abgeschlossen haben. Außerdem musst du [Node.js](https://nodejs.org) installieren und die [Server-API](../api/README.md) lokal ausführen, um Kontodaten zu verwalten.
Du kannst testen, ob der Server ordnungsgemäß läuft, indem du diesen Befehl in einem Terminal ausführst:
@ -32,32 +32,32 @@ curl http://localhost:5000/api
---
## State Management überdenken
## Zustandsmanagement überdenken
In der [vorherigen Lektion](../3-data/README.md) haben wir ein grundlegendes Konzept des States in unserer App eingeführt, mit der globalen `account`-Variable, die die Bankdaten des aktuell angemeldeten Benutzers enthält. Unsere aktuelle Implementierung weist jedoch einige Schwächen auf. Versuche, die Seite zu aktualisieren, während du auf dem Dashboard bist. Was passiert?
In der [vorherigen Lektion](../3-data/README.md) haben wir ein grundlegendes Konzept des Zustands in unserer App eingeführt, mit der globalen Variablen `account`, die die Bankdaten des aktuell angemeldeten Benutzers enthält. Unsere aktuelle Implementierung weist jedoch einige Schwächen auf. Versuche, die Seite zu aktualisieren, während du dich auf dem Dashboard befindest. Was passiert?
Es gibt drei Probleme mit dem aktuellen Code:
- Der State wird nicht gespeichert, da ein Browser-Refresh dich zurück zur Login-Seite bringt.
- Es gibt mehrere Funktionen, die den State ändern. Wenn die App wächst, kann es schwierig werden, die Änderungen nachzuverfolgen, und es ist leicht, zu vergessen, eine Aktualisierung vorzunehmen.
- Der State wird nicht bereinigt, sodass die Kontodaten noch vorhanden sind, wenn du auf *Logout* klickst, obwohl du auf der Login-Seite bist.
- Der Zustand wird nicht gespeichert, da ein Browser-Refresh dich zurück zur Login-Seite bringt.
- Es gibt mehrere Funktionen, die den Zustand ändern. Mit dem Wachstum der App wird es schwierig, die Änderungen nachzuverfolgen, und es ist leicht, das Aktualisieren einer Funktion zu vergessen.
- Der Zustand wird nicht bereinigt, sodass die Kontodaten beim Klicken auf *Logout* weiterhin vorhanden sind, obwohl du dich auf der Login-Seite befindest.
Wir könnten unseren Code aktualisieren, um diese Probleme einzeln anzugehen, aber das würde zu mehr Code-Duplikation führen und die App komplexer und schwerer wartbar machen. Oder wir könnten uns ein paar Minuten Zeit nehmen und unsere Strategie überdenken.
> Welche Probleme versuchen wir hier wirklich zu lösen?
[State Management](https://en.wikipedia.org/wiki/State_management) dreht sich darum, einen guten Ansatz zu finden, um diese zwei spezifischen Probleme zu lösen:
[Zustandsmanagement](https://de.wikipedia.org/wiki/State_Management) dreht sich darum, einen guten Ansatz zu finden, um diese beiden spezifischen Probleme zu lösen:
- Wie können die Datenflüsse in einer App verständlich gehalten werden?
- Wie kann der State immer mit der Benutzeroberfläche synchronisiert werden (und umgekehrt)?
- Wie kann man die Datenflüsse in einer App verständlich halten?
- Wie kann man sicherstellen, dass die Zustandsdaten immer mit der Benutzeroberfläche synchronisiert sind (und umgekehrt)?
Sobald diese Probleme gelöst sind, könnten andere Probleme entweder bereits behoben sein oder leichter zu lösen sein. Es gibt viele mögliche Ansätze, um diese Probleme zu lösen, aber wir werden eine gängige Lösung verwenden, die darin besteht, **die Daten und die Möglichkeiten, sie zu ändern, zu zentralisieren**. Die Datenflüsse würden wie folgt aussehen:
Sobald diese Probleme gelöst sind, könnten andere Probleme entweder bereits behoben sein oder leichter zu lösen sein. Es gibt viele mögliche Ansätze zur Lösung dieser Probleme, aber wir werden eine gängige Lösung verwenden, die darin besteht, **die Daten und die Möglichkeiten, sie zu ändern, zu zentralisieren**. Die Datenflüsse würden so aussehen:


> Wir behandeln hier nicht den Teil, bei dem die Daten automatisch die Ansicht aktualisieren, da dies mit fortgeschrittenen Konzepten der [Reaktiven Programmierung](https://en.wikipedia.org/wiki/Reactive_programming) verbunden ist. Es ist ein gutes Thema für einen tiefergehenden Einstieg.
> Wir werden hier nicht den Teil behandeln, bei dem die Daten automatisch die Ansicht aktualisieren, da dies mit fortgeschritteneren Konzepten der [reaktiven Programmierung](https://de.wikipedia.org/wiki/Reaktive_Programmierung) verbunden ist. Es ist ein gutes Thema für ein tiefergehendes Studium.
✅ Es gibt viele Bibliotheken mit unterschiedlichen Ansätzen für das State Management, [Redux](https://redux.js.org) ist eine beliebte Option. Schau dir die Konzepte und Muster an, die verwendet werden, da sie oft eine gute Möglichkeit bieten, zu lernen, welche potenziellen Probleme in großen Web-Apps auftreten können und wie sie gelöst werden können.
✅ Es gibt viele Bibliotheken mit unterschiedlichen Ansätzen zum Zustandsmanagement, [Redux](https://redux.js.org) ist eine beliebte Option. Schau dir die Konzepte und Muster an, da sie oft eine gute Möglichkeit bieten, potenzielle Probleme in großen Web-Apps zu verstehen und wie sie gelöst werden können.
### Aufgabe
@ -75,9 +75,9 @@ let state = {
};
```
Die Idee ist, *alle Daten unserer App* in einem einzigen State-Objekt zu zentralisieren. Wir haben derzeit nur `account` im State, sodass sich nicht viel ändert, aber es schafft eine Grundlage für Weiterentwicklungen.
Die Idee ist, *alle App-Daten* in einem einzigen Zustandsobjekt zu zentralisieren. Wir haben derzeit nur `account` im Zustand, daher ändert sich nicht viel, aber es schafft eine Grundlage für zukünftige Erweiterungen.
Wir müssen auch die Funktionen aktualisieren, die es verwenden. In den Funktionen `register()` und `login()` ersetze`account = ...` durch `state.account = ...`;
Wir müssen auch die Funktionen aktualisieren, die es verwenden. Ersetze in den Funktionen `register()` und `login()``account = ...` durch `state.account = ...`;
Füge am Anfang der Funktion `updateDashboard()` diese Zeile hinzu:
@ -85,13 +85,13 @@ Füge am Anfang der Funktion `updateDashboard()` diese Zeile hinzu:
const account = state.account;
```
Dieses Refactoring hat für sich genommen nicht viele Verbesserungen gebracht, aber die Idee war, die Grundlage für die nächsten Änderungen zu schaffen.
Dieses Refactoring allein bringt noch keine großen Verbesserungen, aber die Idee war, die Grundlage für die nächsten Änderungen zu schaffen.
## Datenänderungen verfolgen
Jetzt, da wir das `state`-Objekt eingerichtet haben, um unsere Daten zu speichern, ist der nächste Schritt, die Updates zu zentralisieren. Ziel ist es, Änderungen und deren Zeitpunkte leichter nachverfolgen zu können.
Nachdem wir das `state`-Objekt eingerichtet haben, um unsere Daten zu speichern, besteht der nächste Schritt darin, die Aktualisierungen zu zentralisieren. Ziel ist es, Änderungen und deren Zeitpunkt leichter nachverfolgen zu können.
Um zu vermeiden, dass Änderungen am `state`-Objekt vorgenommen werden, ist es auch eine gute Praxis, es als [*unveränderlich*](https://en.wikipedia.org/wiki/Immutable_object) zu betrachten, was bedeutet, dass es überhaupt nicht geändert werden kann. Das bedeutet auch, dass du ein neues State-Objekt erstellen musst, wenn du etwas daran ändern möchtest. Dadurch wird eine Schutzmaßnahme gegen potenziell unerwünschte [Nebenwirkungen](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) geschaffen und gleichzeitig die Möglichkeit für neue Funktionen in deiner App eröffnet, wie z. B. die Implementierung von Undo/Redo. Außerdem wird das Debuggen erleichtert. Du könntest beispielsweise jede Änderung am State protokollieren und eine Historie der Änderungen führen, um die Quelle eines Fehlers zu verstehen.
Um zu vermeiden, dass Änderungen direkt am `state`-Objekt vorgenommen werden, ist es auch eine gute Praxis, es als [*unveränderlich*](https://de.wikipedia.org/wiki/Unver%C3%A4nderliches_Objekt) zu betrachten, was bedeutet, dass es überhaupt nicht geändert werden kann. Das bedeutet auch, dass du ein neues Zustandsobjekt erstellen musst, wenn du etwas daran ändern möchtest. Auf diese Weise schützt du dich vor potenziell unerwünschten [Seiteneffekten](https://de.wikipedia.org/wiki/Seiteneffekt_(Informatik)) und eröffnest Möglichkeiten für neue Funktionen in deiner App, wie z. B. die Implementierung von Rückgängig/Wiederholen, während du gleichzeitig das Debuggen erleichterst. Zum Beispiel könntest du jede Änderung am Zustand protokollieren und eine Historie der Änderungen führen, um die Quelle eines Fehlers zu verstehen.
In JavaScript kannst du [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) verwenden, um eine unveränderliche Version eines Objekts zu erstellen. Wenn du versuchst, Änderungen an einem unveränderlichen Objekt vorzunehmen, wird eine Ausnahme ausgelöst.
@ -99,7 +99,7 @@ In JavaScript kannst du [`Object.freeze()`](https://developer.mozilla.org/docs/W
### Aufgabe
Lass uns eine neue Funktion `updateState()` erstellen:
Erstelle eine neue Funktion `updateState()`:
```js
function updateState(property, newData) {
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
In dieser Funktion erstellen wir ein neues State-Objekt und kopieren Daten aus dem vorherigen State mithilfe des [*Spread (`...`) Operators*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Dann überschreiben wir eine bestimmte Eigenschaft des State-Objekts mit den neuen Daten, indem wir die [Bracket-Notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` für die Zuweisung verwenden. Schließlich sperren wir das Objekt, um Änderungen zu verhindern, indem wir `Object.freeze()` verwenden. Derzeit haben wir nur die `account`-Eigenschaft im State gespeichert, aber mit diesem Ansatz kannst du so viele Eigenschaften wie nötig im State hinzufügen.
In dieser Funktion erstellen wir ein neues Zustandsobjekt und kopieren Daten aus dem vorherigen Zustand mithilfe des [*Spread-Operators (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Dann überschreiben wir eine bestimmte Eigenschaft des Zustandsobjekts mit den neuen Daten, indem wir die [Klammernotation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` für die Zuweisung verwenden. Schließlich sperren wir das Objekt, um Änderungen mit `Object.freeze()` zu verhindern. Derzeit speichern wir nur die `account`-Eigenschaft im Zustand, aber mit diesem Ansatz kannst du so viele Eigenschaften hinzufügen, wie du benötigst.
Wir aktualisieren auch die `state`-Initialisierung, um sicherzustellen, dass der anfängliche State ebenfalls eingefroren ist:
Wir aktualisieren auch die `state`-Initialisierung, um sicherzustellen, dass der Anfangszustand ebenfalls eingefroren ist:
```js
let state = Object.freeze({
@ -120,19 +120,19 @@ let state = Object.freeze({
});
```
Danach aktualisieren wir die Funktion `register`, indem wir die Zuweisung `state.account = result;` durch Folgendes ersetzen:
Danach aktualisiere die `register`-Funktion, indem du die Zuweisung `state.account = result;` durch Folgendes ersetzt:
```js
updateState('account', result);
```
Das Gleiche machen wir mit der Funktion `login`, indem wir `state.account = data;` durch Folgendes ersetzen:
Mache dasselbe mit der `login`-Funktion, indem du `state.account = data;` durch Folgendes ersetzt:
```js
updateState('account', data);
```
Wir nutzen die Gelegenheit, um das Problem zu beheben, dass die Kontodaten nicht gelöscht werden, wenn der Benutzer auf *Logout* klickt.
Wir nutzen die Gelegenheit, um das Problem zu beheben, dass Kontodaten nicht gelöscht werden, wenn der Benutzer auf *Logout* klickt.
Erstelle eine neue Funktion `logout()`:
@ -143,35 +143,35 @@ function logout() {
}
```
Ersetze in `updateDashboard()` die Umleitung `return navigate('/login');` durch `return logout();`;
Ersetze in `updateDashboard()` die Weiterleitung `return navigate('/login');` durch `return logout();`;
Versuche, ein neues Konto zu registrieren, dich auszuloggen und erneut einzuloggen, um zu überprüfen, ob alles weiterhin korrekt funktioniert.
Versuche, ein neues Konto zu registrieren, dich abzumelden und erneut anzumelden, um zu überprüfen, ob alles noch korrekt funktioniert.
> Tipp: Du kannst dir alle State-Änderungen ansehen, indem du `console.log(state)` am Ende von `updateState()` hinzufügst und die Konsole in den Entwicklertools deines Browsers öffnest.
> Tipp: Du kannst alle Zustandsänderungen anzeigen, indem du `console.log(state)` am Ende von `updateState()` hinzufügst und die Konsole in den Entwicklerwerkzeugen deines Browsers öffnest.
## State speichern
## Zustand speichern
Die meisten Web-Apps müssen Daten speichern, um korrekt zu funktionieren. Alle kritischen Daten werden normalerweise in einer Datenbank gespeichert und über eine Server-API abgerufen, wie z. B. die Benutzerdaten in unserem Fall. Aber manchmal ist es auch interessant, einige Daten in der Client-App zu speichern, die in deinem Browser läuft, um eine bessere Benutzererfahrung oder eine verbesserte Ladeleistung zu erzielen.
Die meisten Web-Apps müssen Daten speichern, um korrekt zu funktionieren. Alle kritischen Daten werden normalerweise in einer Datenbank gespeichert und über eine Server-API abgerufen, wie z. B. die Benutzerdaten in unserem Fall. Aber manchmal ist es auch interessant, einige Daten in der Client-App zu speichern, die in deinem Browser läuft, um eine bessere Benutzererfahrung zu bieten oder die Ladeleistung zu verbessern.
Wenn du Daten in deinem Browser speichern möchtest, gibt es einige wichtige Fragen, die du dir stellen solltest:
- *Sind die Daten sensibel?* Du solltest vermeiden, sensible Daten wie Benutzerpasswörter auf dem Client zu speichern.
- *Wie lange möchtest du diese Daten behalten?* Planst du, auf diese Daten nur während der aktuellen Sitzung zuzugreifen, oder möchtest du sie dauerhaft speichern?
- *Wie lange möchtest du diese Daten aufbewahren?* Planst du, auf diese Daten nur während der aktuellen Sitzung zuzugreifen, oder möchtest du, dass sie dauerhaft gespeichert werden?
Es gibt verschiedene Möglichkeiten, Informationen innerhalb einer Web-App zu speichern, abhängig davon, was du erreichen möchtest. Zum Beispiel kannst du die URLs verwenden, um eine Suchanfrage zu speichern und sie zwischen Benutzern teilbar zu machen. Du kannst auch [HTTP-Cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) verwenden, wenn die Daten mit dem Server geteilt werden müssen, wie z. B. [Authentifizierungsinformationen](https://en.wikipedia.org/wiki/Authentication).
Es gibt mehrere Möglichkeiten, Informationen in einer Web-App zu speichern, je nachdem, was du erreichen möchtest. Zum Beispiel kannst du die URLs verwenden, um eine Suchanfrage zu speichern und sie zwischen Benutzern teilbar zu machen. Du kannst auch [HTTP-Cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) verwenden, wenn die Daten mit dem Server geteilt werden müssen, wie z. B. [Authentifizierungsinformationen](https://de.wikipedia.org/wiki/Authentifizierung).
Eine andere Option ist die Verwendung einer der vielen Browser-APIs zum Speichern von Daten. Zwei davon sind besonders interessant:
Eine weitere Option ist die Verwendung einer der vielen Browser-APIs zum Speichern von Daten. Zwei davon sind besonders interessant:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): ein [Key/Value Store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), der es ermöglicht, daten spezifisch für die aktuelle Website über verschiedene Sitzungen hinweg zu speichern. Die gespeicherten Daten verfallen nie.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funktioniert genauso wie `localStorage`, außer dass die darin gespeicherten Daten gelöscht werden, wenn die Sitzung endet (wenn der Browser geschlossen wird).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): Ein [Key/Value-Store](https://de.wikipedia.org/wiki/Key-Value-Datenbank), der es ermöglicht, datenbankspezifische Daten für die aktuelle Website über verschiedene Sitzungen hinweg zu speichern. Die darin gespeicherten Daten verfallen nie.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): Funktioniert genauso wie `localStorage`, außer dass die darin gespeicherten Daten gelöscht werden, wenn die Sitzung endet (wenn der Browser geschlossen wird).
Beachte, dass beide APIs nur [Strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) speichern können. Wenn du komplexe Objekte speichern möchtest, musst du sie in das [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-Format serialisieren, indem du [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) verwendest.
Beachte, dass beide APIs nur das Speichern von [Strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) erlauben. Wenn du komplexe Objekte speichern möchtest, musst du sie in das [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-Format serialisieren, indem du [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) verwendest.
✅ Wenn du eine Web-App erstellen möchtest, die nicht mit einem Server arbeitet, ist es auch möglich, eine Datenbank auf dem Client mit der [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) zu erstellen. Diese ist für fortgeschrittene Anwendungsfälle oder wenn du eine erhebliche Menge an Daten speichern musst, da sie komplexer zu verwenden ist.
✅ Wenn du eine Web-App erstellen möchtest, die ohne Server funktioniert, ist es auch möglich, eine Datenbank auf dem Client mit der [`IndexedDB`-API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) zu erstellen. Diese ist für fortgeschrittene Anwendungsfälle oder wenn du eine erhebliche Menge an Daten speichern musst, da sie komplexer zu verwenden ist.
### Aufgabe
Wir möchten, dass unsere Benutzer eingeloggt bleiben, bis sie explizit auf die *Logout*-Schaltfläche klicken. Daher verwenden wir `localStorage`, um die Kontodaten zu speichern. Zunächst definieren wir einen Schlüssel, den wir zum Speichern unserer Daten verwenden.
Wir möchten, dass unsere Benutzer angemeldet bleiben, bis sie explizit auf die Schaltfläche *Logout* klicken. Daher verwenden wir `localStorage`, um die Kontodaten zu speichern. Definiere zunächst einen Schlüssel, den wir zum Speichern unserer Daten verwenden.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Füge dann diese Zeile am Ende der Funktion `updateState()` hinzu:
Damit werden die Benutzerdaten gespeichert und sind immer auf dem neuesten Stand, da wir zuvor alle State-Updates zentralisiert haben. Hier beginnen wir, von all unseren vorherigen Refactorings zu profitieren 🙂.
Damit werden die Benutzerdaten gespeichert und bleiben immer auf dem neuesten Stand, da wir zuvor alle Zustandsaktualisierungen zentralisiert haben. Hier beginnen wir, von all unseren vorherigen Refactorings zu profitieren 🙂.
Da die Daten gespeichert werden, müssen wir auch dafür sorgen, dass sie wiederhergestellt werden, wenn die App geladen wird. Da wir mehr Initialisierungscode haben werden, ist es eine gute Idee, eine neue Funktion `init` zu erstellen, die auch unseren vorherigen Code am Ende von `app.js` enthält:
Da die Daten gespeichert werden, müssen wir uns auch darum kümmern, sie wiederherzustellen, wenn die App geladen wird. Da wir nun mehr Initialisierungscode haben, könnte es eine gute Idee sein, eine neue Funktion `init` zu erstellen, die auch unseren bisherigen Code am Ende von `app.js` enthält:
```js
function init() {
@ -202,11 +202,11 @@ function init() {
init();
```
Hier rufen wir die gespeicherten Daten ab, und wenn welche vorhanden sind, aktualisieren wir den State entsprechend. Es ist wichtig, dies *vor* der Aktualisierung der Route zu tun, da es Code geben könnte, der während der Seitenaktualisierung auf den State angewiesen ist.
Hier rufen wir die gespeicherten Daten ab, und falls welche vorhanden sind, aktualisieren wir den Zustand entsprechend. Es ist wichtig, dies *vor* der Aktualisierung der Route zu tun, da es möglicherweise Code gibt, der während der Seitenaktualisierung auf den Zustand angewiesen ist.
Wir können auch die *Dashboard*-Seite zur Standardseite unserer Anwendung machen, da wir jetzt die Kontodaten speichern. Wenn keine Daten gefunden werden, kümmert sich das Dashboard ohnehin darum, zur *Login*-Seite umzuleiten. Ersetze in `updateRoute()` den Fallback `return navigate('/login');` durch `return navigate('/dashboard');`.
Wir können auch die *Dashboard*-Seite zur Standardseite unserer Anwendung machen, da wir jetzt die Kontodaten speichern. Wenn keine Daten gefunden werden, kümmert sich das Dashboard ohnehin um die Weiterleitung zur *Login*-Seite. Ersetze in `updateRoute()` den Fallback `return navigate('/login');` durch `return navigate('/dashboard');`.
Logge dich jetzt in die App ein und versuche, die Seite zu aktualisieren. Du solltest auf dem Dashboard bleiben. Mit diesem Update haben wir alle unsere anfänglichen Probleme gelöst...
Melde dich jetzt in der App an und versuche, die Seite zu aktualisieren. Du solltest auf dem Dashboard bleiben. Mit diesem Update haben wir alle unsere anfänglichen Probleme gelöst...
Versuche jetzt, die Dashboard-Seite im Browser zu aktualisieren. Was passiert? Siehst du die neue Transaktion?
Versuche jetzt, die Dashboard-Seite in deinem Browser zu aktualisieren. Was passiert? Siehst du die neue Transaktion?
Der State wird dank `localStorage` unbegrenzt gespeichert, aber das bedeutet auch, dass er nie aktualisiert wird, bis du dich aus der App ausloggst und wieder einloggst!
Der Zustand wird dank `localStorage` unbegrenzt gespeichert, aber das bedeutet auch, dass er nie aktualisiert wird, bis du dich aus der App abmeldest und wieder anmeldest!
Eine mögliche Strategie, um das zu beheben, ist, die Kontodaten jedes Mal neu zu laden, wenn das Dashboard geladen wird, um veraltete Daten zu vermeiden.
Eine mögliche Strategie, um das zu beheben, besteht darin, die Kontodaten jedes Mal neu zu laden, wenn das Dashboard geladen wird, um veraltete Daten zu vermeiden.
### Aufgabe
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Diese Methode überprüft, ob wir derzeit eingeloggt sind, und lädt dann die Kontodaten vom Server neu.
Diese Methode überprüft, ob wir derzeit angemeldet sind, und lädt dann die Kontodaten vom Server neu.
Erstelle eine weitere Funktion namens `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Diese Funktion aktualisiert die Kontodaten und kümmert sich dann um die Aktualisierung des HTML des Dashboard-Seite. Sie ist das, was wir aufrufen müssen, wenn die Dashboard-Route geladen wird. Aktualisiere die Routen-Definition mit:
Diese Funktion aktualisiert die Kontodaten und kümmert sich dann um die Aktualisierung des HTMLs der Dashboard-Seite. Sie ist das, was wir aufrufen müssen, wenn die Dashboard-Route geladen wird. Aktualisiere die Routendefinition mit:
```js
const routes = {
@ -273,22 +273,22 @@ Versuche jetzt, das Dashboard zu aktualisieren. Es sollte die aktualisierten Kon
## 🚀 Herausforderung
Da wir die Kontodaten jedes Mal neu laden, wenn das Dashboard geladen wird, denkst du, dass wir immer noch *alle Kontodaten* speichern müssen?
Da wir die Kontodaten jedes Mal neu laden, wenn das Dashboard geladen wird, denkst du, dass wir *alle Kontodaten* weiterhin speichern müssen?
Versuche gemeinsam zu arbeiten, um zu ändern, was in `localStorage` gespeichert und geladen wird, sodass nur das gespeichert wird, was absolut notwendig ist, damit die App funktioniert.
Versuche gemeinsam zu erarbeiten, was minimal in `localStorage` gespeichert und geladen werden muss, damit die App funktioniert.
## Quiz nach der Vorlesung
[Quiz nach der Vorlesung](https://ff-quizzes.netlify.app/web/quiz/48)
## Aufgabe
[Implementieren Sie den Dialog "Transaktion hinzufügen"](assignment.md)
Hier ist ein Beispielergebnis nach Abschluss der Aufgabe:


---
**Haftungsausschluss**:
Dieses Dokument wurde mit dem KI-Übersetzungsdienst [Co-op Translator](https://github.com/Azure/co-op-translator) übersetzt. Obwohl wir uns um Genauigkeit bemühen, beachten Sie bitte, dass automatisierte Übersetzungen Fehler oder Ungenauigkeiten enthalten können. Das Originaldokument in seiner ursprünglichen Sprache sollte als maßgebliche Quelle betrachtet werden. Für kritische Informationen wird eine professionelle menschliche Übersetzung empfohlen. Wir übernehmen keine Haftung für Missverständnisse oder Fehlinterpretationen, die sich aus der Nutzung dieser Übersetzung ergeben.
Dieses Dokument wurde mit dem KI-Übersetzungsdienst [Co-op Translator](https://github.com/Azure/co-op-translator) übersetzt. Obwohl wir uns um Genauigkeit bemühen, weisen wir darauf hin, dass automatisierte Übersetzungen Fehler oder Ungenauigkeiten enthalten können. Das Originaldokument in seiner ursprünglichen Sprache sollte als maßgebliche Quelle betrachtet werden. Für kritische Informationen wird eine professionelle menschliche Übersetzung empfohlen. Wir übernehmen keine Haftung für Missverständnisse oder Fehlinterpretationen, die sich aus der Nutzung dieser Übersetzung ergeben.
# Δημιουργία Εφαρμογής Τραπεζικής Μέρος 4: Έννοιες Διαχείρισης Κατάστασης
## Ερωτηματολόγιο Πριν το Μάθημα
## Κουίζ Πριν το Μάθημα
[Ερωτηματολόγιο πριν το μάθημα](https://ff-quizzes.netlify.app/web/quiz/47)
[Κουίζ πριν το μάθημα](https://ff-quizzes.netlify.app/web/quiz/47)
### Εισαγωγή
Καθώς μια διαδικτυακή εφαρμογή μεγαλώνει, γίνεται πρόκληση να παρακολουθείς όλες τις ροές δεδομένων. Ποιος κώδικας λαμβάνει τα δεδομένα, ποια σελίδα τα χρησιμοποιεί, πού και πότε πρέπει να ενημερωθούν... είναι εύκολο να καταλήξεις με ακατάστατο κώδικα που είναι δύσκολο να συντηρηθεί. Αυτό ισχύει ιδιαίτερα όταν χρειάζεται να μοιραστείς δεδομένα μεταξύ διαφορετικών σελίδων της εφαρμογής σου, όπως τα δεδομένα χρήστη. Η έννοια της *διαχείρισης κατάστασης* υπήρχε πάντα σε κάθε είδους προγράμματα, αλλά καθώς οι διαδικτυακές εφαρμογές γίνονται όλο και πιο περίπλοκες, είναι πλέον ένα βασικό σημείο που πρέπει να λαμβάνεται υπόψη κατά την ανάπτυξη.
Καθώς μια διαδικτυακή εφαρμογή μεγαλώνει, γίνεται πρόκληση να παρακολουθείς όλες τις ροές δεδομένων. Ποιος κώδικας λαμβάνει τα δεδομένα, ποια σελίδα τα χρησιμοποιεί, πού και πότε πρέπει να ενημερωθούν... είναι εύκολο να καταλήξεις με μπερδεμένο κώδικα που είναι δύσκολο να συντηρηθεί. Αυτό ισχύει ιδιαίτερα όταν χρειάζεται να μοιραστείς δεδομένα μεταξύ διαφορετικών σελίδων της εφαρμογής σου, όπως τα δεδομένα χρήστη. Η έννοια της *διαχείρισης κατάστασης* υπήρχε πάντα σε κάθε είδους προγράμματα, αλλά καθώς οι διαδικτυακές εφαρμογές γίνονται όλο και πιο περίπλοκες, είναι πλέον ένα βασικό σημείο που πρέπει να λαμβάνεται υπόψη κατά την ανάπτυξη.
Σε αυτό το τελευταίο μέρος, θα εξετάσουμε την εφαρμογή που δημιουργήσαμε γιαναεπανεξετάσουμε πώς διαχειρίζεται η κατάσταση, επιτρέποντας την υποστήριξη ανανέωσης του προγράμματος περιήγησης οποιαδήποτε στιγμή και τη διατήρηση δεδομένων μεταξύ των συνεδριών χρήστη.
Σε αυτό το τελευταίο μέρος, θα επανεξετάσουμε την εφαρμογή που δημιουργήσαμε γιανααναθεωρήσουμε πώς διαχειρίζεται η κατάσταση, επιτρέποντας την υποστήριξη ανανέωσης του προγράμματος περιήγησης οποιαδήποτε στιγμή και τη διατήρηση δεδομένων μεταξύ των συνεδριών χρήστη.
### Προαπαιτούμενα
Πρέπει να έχεις ολοκληρώσει το μέρος [ανάκτησης δεδομένων](../3-data/README.md) της διαδικτυακής εφαρμογής για αυτό το μάθημα. Πρέπει επίσης να εγκαταστήσεις το [Node.js](https://nodejs.org) και να [εκτελέσεις το API του διακομιστή](../api/README.md) τοπικά ώστε να μπορείς να διαχειριστείς τα δεδομένα λογαριασμού.
Πρέπει να έχεις ολοκληρώσει το μέρος [ανάκτησης δεδομένων](../3-data/README.md) της διαδικτυακής εφαρμογής για αυτό το μάθημα. Πρέπει επίσης να εγκαταστήσεις το [Node.js](https://nodejs.org) και να [εκτελέσεις το API διακομιστή](../api/README.md) τοπικά ώστε να μπορείς να διαχειριστείς τα δεδομένα λογαριασμού.
Μπορείς ναδοκιμάσεις ότι ο διακομιστής λειτουργεί σωστά εκτελώντας αυτήν την εντολή σε ένα τερματικό:
Μπορείς ναελέγξεις ότι ο διακομιστής λειτουργεί σωστά εκτελώντας αυτήν την εντολή σε ένα τερματικό:
```sh
curl http://localhost:5000/api
@ -32,26 +32,26 @@ curl http://localhost:5000/api
---
## Επανεξέταση της διαχείρισης κατάστασης
## Αναθεώρηση της διαχείρισης κατάστασης
Στο [προηγούμενο μάθημα](../3-data/README.md), εισαγάγαμε μια βασική έννοια κατάστασης στην εφαρμογή μας με τη χρήση της παγκόσμιας μεταβλητής `account`, η οποία περιέχει τα τραπεζικά δεδομένα του χρήστη που είναι συνδεδεμένος. Ωστόσο, η τρέχουσα υλοποίησή μας έχει κάποια προβλήματα. Δοκίμασε να ανανεώσεις τη σελίδα όταν βρίσκεσαι στον πίνακα ελέγχου. Τι συμβαίνει;
Υπάρχουν 3 προβλήματα με τον τρέχοντα κώδικα:
- Η κατάσταση δεν διατηρείται, καθώς η ανανέωση του προγράμματος περιήγησης σε επιστρέφει στη σελίδα σύνδεσης.
- Υπάρχουν πολλές συναρτήσεις που τροποποιούν την κατάσταση. Καθώς η εφαρμογή μεγαλώνει, μπορεί να γίνει δύσκολο να παρακολουθήσεις τις αλλαγές και είναι εύκολο να ξεχάσεις να ενημερώσεις μία.
- Η κατάσταση δεν καθαρίζεται, οπότε όταν κάνεις κλικ στο *Αποσύνδεση*, τα δεδομένα του λογαριασμού παραμένουν εκεί, παρόλο που βρίσκεσαι στη σελίδα σύνδεσης.
- Η κατάσταση δεν διατηρείται, καθώς μια ανανέωση του προγράμματος περιήγησης σε επιστρέφει στη σελίδα σύνδεσης.
- Υπάρχουν πολλές συναρτήσεις που τροποποιούν την κατάσταση. Καθώς η εφαρμογή μεγαλώνει, αυτό μπορεί να δυσκολέψει την παρακολούθηση των αλλαγών και είναι εύκολο να ξεχάσεις να ενημερώσεις κάτι.
- Η κατάσταση δεν καθαρίζεται, οπότε όταν κάνεις κλικ στο *Αποσύνδεση*, τα δεδομένα του λογαριασμού παραμένουν, παρόλο που βρίσκεσαι στη σελίδα σύνδεσης.
Θα μπορούσαμε να ενημερώσουμε τον κώδικα μας γιανα αντιμετωπίσουμε αυτά τα προβλήματα ένα προς ένα, αλλά αυτό θα δημιουργούσε περισσότερη επανάληψη κώδικα και θα έκανε την εφαρμογή πιο περίπλοκη και δύσκολη στη συντήρηση. Ή θα μπορούσαμε να σταματήσουμε για λίγα λεπτά και ναεπανεξετάσουμε τη στρατηγική μας.
Θα μπορούσαμε να ενημερώσουμε τον κώδικά μας γιανα αντιμετωπίσουμε αυτά τα προβλήματα ένα προς ένα, αλλά αυτό θα δημιουργούσε περισσότερη επανάληψη κώδικα και θα έκανε την εφαρμογή πιο περίπλοκη και δύσκολη στη συντήρηση. Ή θα μπορούσαμε να σταματήσουμε για λίγα λεπτά και νααναθεωρήσουμε τη στρατηγική μας.
> Ποια προβλήματα προσπαθούμε πραγματικά να λύσουμε εδώ;
Η [διαχείριση κατάστασης](https://en.wikipedia.org/wiki/State_management) αφορά την εύρεση μιας καλής προσέγγισης για την επίλυση αυτών των δύο συγκεκριμένων προβλημάτων:
- Πώς να διατηρήσουμε τις ροές δεδομένων σε μια εφαρμογή κατανοητές;
- Πώς να διατηρήσουμε τα δεδομένα κατάστασης πάντα συγχρονισμένα με τη διεπαφή χρήστη (και αντίστροφα);
- Πώς να διατηρήσουμε τα δεδομένα κατάστασης πάντα συγχρονισμένα με τη διεπαφή χρήστη (και το αντίστροφο);
Μόλις φροντίσεις αυτά τα ζητήματα, οποιαδήποτε άλλα προβλήματα μπορεί να έχεις είτε θα έχουν ήδη λυθεί είτε θα έχουν γίνει ευκολότερα να λυθούν. Υπάρχουν πολλές πιθανές προσεγγίσεις για την επίλυση αυτών των προβλημάτων, αλλά θα ακολουθήσουμε μια κοινή λύση που συνίσταται στο **να κεντροποιήσουμε τα δεδομένα και τους τρόπους αλλαγής τους**. Οι ροές δεδομένων θα λειτουργούν ως εξής:
Αφού αντιμετωπίσεις αυτά τα ζητήματα, οποιαδήποτε άλλα προβλήματα μπορεί να έχουν ήδη λυθεί ή να έχουν γίνει ευκολότερα να λυθούν. Υπάρχουν πολλές πιθανές προσεγγίσεις για την επίλυση αυτών των προβλημάτων, αλλά θα ακολουθήσουμε μια κοινή λύση που συνίσταται στο **να κεντροποιήσουμε τα δεδομένα και τους τρόπους αλλαγής τους**. Οι ροές δεδομένων θα λειτουργούν ως εξής:

@ -75,9 +75,9 @@ let state = {
};
```
Η ιδέα είναι να*κεντροποιήσουμε* όλα τα δεδομένα της εφαρμογής μας σε ένα μόνο αντικείμενο κατάστασης. Προς το παρόν έχουμε μόνο το `account` στην κατάσταση, οπότε δεν αλλάζει πολύ, αλλά δημιουργεί μια βάση για μελλοντικές εξελίξεις.
Η ιδέα είναι να*κεντροποιήσουμε* όλα τα δεδομένα της εφαρμογής μας σε ένα μόνο αντικείμενο κατάστασης. Προς το παρόν έχουμε μόνο το `account` στην κατάσταση, οπότε δεν αλλάζει πολλά, αλλά δημιουργεί μια βάση για μελλοντικές εξελίξεις.
Πρέπει επίσης να ενημερώσουμε τις συναρτήσεις που το χρησιμοποιούν. Στις συναρτήσεις `register()` και `login()`, αντικατάστησε το `account = ...` με `state.account = ...`;
Πρέπει επίσης να ενημερώσουμε τις συναρτήσεις που το χρησιμοποιούν. Στις συναρτήσεις `register()` και `login()`, αντικατάστησε το `account = ...` με `state.account = ...`.
Στην αρχή της συνάρτησης `updateDashboard()`, πρόσθεσε αυτή τη γραμμή:
@ -89,9 +89,9 @@ const account = state.account;
## Παρακολούθηση αλλαγών δεδομένων
Τώρα που έχουμε θέσει το αντικείμενο `state`για την αποθήκευση των δεδομένων μας, το επόμενο βήμα είναι να κεντροποιήσουμε τις ενημερώσεις. Στόχος είναι να γίνει ευκολότερονα παρακολουθούμε οποιεσδήποτε αλλαγές και πότε συμβαίνουν.
Τώρα που έχουμε θέσει το αντικείμενο `state`για την αποθήκευση των δεδομένων μας, το επόμενο βήμα είναι να κεντροποιήσουμε τις ενημερώσεις. Στόχος είναι να γίνει ευκολότερη η παρακολούθηση οποιωνδήποτε αλλαγών και πότε αυτές συμβαίνουν.
Για να αποφύγουμε την πραγματοποίηση αλλαγών στο αντικείμενο `state`, είναι επίσης καλή πρακτική να το θεωρούμε [*αμετάβλητο*](https://en.wikipedia.org/wiki/Immutable_object), που σημαίνει ότι δεν μπορεί να τροποποιηθεί καθόλου. Αυτό σημαίνει επίσης ότι πρέπει να δημιουργήσεις ένα νέο αντικείμενο κατάστασης αν θέλεις να αλλάξεις κάτι σε αυτό. Με αυτόν τον τρόπο, δημιουργείς προστασία από πιθανές ανεπιθύμητες [παρενέργειες](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) και ανοίγεις δυνατότητες για νέες λειτουργίες στην εφαρμογή σου, όπως η υλοποίηση undo/redo, ενώ ταυτόχρονα διευκολύνεις την αποσφαλμάτωση. Για παράδειγμα, θα μπορούσες να καταγράψεις κάθε αλλαγή που γίνεται στην κατάσταση και να διατηρήσεις ένα ιστορικό των αλλαγών γιανα κατανοήσεις την πηγή ενός σφάλματος.
Για να αποφύγουμε αλλαγές στο αντικείμενο `state`, είναι επίσης καλή πρακτική να το θεωρούμε [*αμετάβλητο*](https://en.wikipedia.org/wiki/Immutable_object), που σημαίνει ότι δεν μπορεί να τροποποιηθεί καθόλου. Αυτό σημαίνει επίσης ότι πρέπει να δημιουργήσεις ένα νέο αντικείμενο κατάστασης αν θέλεις να αλλάξεις κάτι σε αυτό. Με αυτόν τον τρόπο, δημιουργείς μια προστασία από πιθανές ανεπιθύμητες [παρενέργειες](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) και ανοίγεις δυνατότητες για νέες λειτουργίες στην εφαρμογή σου, όπως η υλοποίηση αναίρεσης/επανάληψης, ενώ ταυτόχρονα διευκολύνεις την αποσφαλμάτωση. Για παράδειγμα, θα μπορούσες να καταγράψεις κάθε αλλαγή που γίνεται στην κατάσταση και να διατηρήσεις ένα ιστορικό των αλλαγών γιανα κατανοήσεις την πηγή ενός σφάλματος.
Στην JavaScript, μπορείς να χρησιμοποιήσεις το [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) γιανα δημιουργήσεις μια αμετάβλητη έκδοση ενός αντικειμένου. Αν προσπαθήσεις να κάνεις αλλαγές σε ένα αμετάβλητο αντικείμενο, θα προκληθεί εξαίρεση.
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
Σε αυτή τη συνάρτηση, δημιουργούμε ένα νέο αντικείμενο κατάστασης και αντιγράφουμε δεδομένα από την προηγούμενη κατάσταση χρησιμοποιώντας τον [*τελεστή spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Στη συνέχεια, αντικαθιστούμε μια συγκεκριμένη ιδιότητα του αντικειμένου κατάστασης με τα νέα δεδομένα χρησιμοποιώντας τη [σημειογραφία αγκύλης](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]`για την ανάθεση. Τέλος, κλειδώνουμε το αντικείμενο γιανα αποτρέψουμε τροποποιήσεις χρησιμοποιώντας το `Object.freeze()`. Προς το παρόν, έχουμε μόνο την ιδιότητα `account` αποθηκευμένη στην κατάσταση, αλλά με αυτήν την προσέγγιση μπορείς να προσθέσεις όσες ιδιότητες χρειάζεσαι στην κατάσταση.
Σε αυτή τη συνάρτηση, δημιουργούμε ένα νέο αντικείμενο κατάστασης και αντιγράφουμε δεδομένα από την προηγούμενη κατάσταση χρησιμοποιώντας τον [*τελεστή εξάπλωσης (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Στη συνέχεια, αντικαθιστούμε μια συγκεκριμένη ιδιότητα του αντικειμένου κατάστασης με τα νέα δεδομένα χρησιμοποιώντας τη [σημειογραφία αγκύλης](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]`για την ανάθεση. Τέλος, κλειδώνουμε το αντικείμενο γιανα αποτρέψουμε τροποποιήσεις χρησιμοποιώντας το `Object.freeze()`. Προς το παρόν, έχουμε μόνο την ιδιότητα `account` αποθηκευμένη στην κατάσταση, αλλά με αυτή την προσέγγιση μπορείς να προσθέσεις όσες ιδιότητες χρειάζεσαι.
Θα ενημερώσουμε επίσης την αρχικοποίηση της `state`γιανα βεβαιωθούμε ότι η αρχική κατάσταση είναι επίσης κλειδωμένη:
Θα ενημερώσουμε επίσης την αρχικοποίηση του`state`γιανα διασφαλίσουμε ότι η αρχική κατάσταση είναι επίσης κλειδωμένη:
Τώρα θα εκμεταλλευτούμε την ευκαιρία να διορθώσουμε το πρόβλημα των δεδομένων λογαριασμού που δεν καθαρίζονται όταν ο χρήστης κάνει κλικ στο *Αποσύνδεση*.
Τώρα θα εκμεταλλευτούμε την ευκαιρία να διορθώσουμε το πρόβλημα των δεδομένων λογαριασμού που δεν διαγράφονται όταν ο χρήστης κάνει κλικ στο *Αποσύνδεση*.
Δημιούργησε μια νέα συνάρτηση `logout()`:
@ -143,29 +143,29 @@ function logout() {
}
```
Στην `updateDashboard()`, αντικατάστησε την ανακατεύθυνση `return navigate('/login');` με `return logout();`
Στησυνάρτηση`updateDashboard()`, αντικατάστησε την ανακατεύθυνση `return navigate('/login');` με `return logout();`.
Δοκίμασε να εγγραφείς σε έναν νέο λογαριασμό, να αποσυνδεθείς και να συνδεθείς ξανά γιανα ελέγξεις ότι όλα λειτουργούν σωστά.
Δοκίμασε να εγγραφείς με νέο λογαριασμό, να αποσυνδεθείς και να συνδεθείς ξανά γιανα ελέγξεις ότι όλα λειτουργούν σωστά.
> Συμβουλή: μπορείς να δεις όλες τις αλλαγές κατάστασης προσθέτοντας `console.log(state)` στο τέλος της `updateState()` και ανοίγοντας την κονσόλα στα εργαλεία ανάπτυξης του προγράμματος περιήγησης.
## Διατήρηση της κατάστασης
Οι περισσότερες διαδικτυακές εφαρμογές χρειάζονται να διατηρούν δεδομένα γιανα λειτουργούν σωστά. Όλα τα κρίσιμα δεδομένα συνήθως αποθηκεύονται σε μια βάση δεδομένων και προσπελάζονται μέσω ενός API διακομιστή, όπως τα δεδομένα λογαριασμού χρήστη στην περίπτωσή μας. Αλλά μερικές φορές, είναι επίσης ενδιαφέρον να διατηρείς κάποια δεδομένα στην εφαρμογή πελάτη που εκτελείται στο πρόγραμμα περιήγησης, για καλύτερη εμπειρία χρήστη ή για βελτίωση της απόδοσης φόρτωσης.
Οι περισσότερες διαδικτυακές εφαρμογές χρειάζονται να διατηρούν δεδομένα γιανα λειτουργούν σωστά. Όλα τα κρίσιμα δεδομένα συνήθως αποθηκεύονται σε μια βάση δεδομένων και είναι προσβάσιμα μέσω ενός API διακομιστή, όπως τα δεδομένα λογαριασμού χρήστη στην περίπτωσή μας. Αλλά μερικές φορές, είναι επίσης ενδιαφέρον να διατηρείς κάποια δεδομένα στην εφαρμογή πελάτη που εκτελείται στο πρόγραμμα περιήγησης, για καλύτερη εμπειρία χρήστη ή για βελτίωση της απόδοσης φόρτωσης.
Όταν θέλεις να διατηρήσεις δεδομένα στο πρόγραμμα περιήγησης, υπάρχουν μερικές σημαντικές ερωτήσεις που πρέπει να κάνεις:
- *Είναι τα δεδομένα ευαίσθητα;*Θα πρέπει να αποφεύγεις την αποθήκευση οποιωνδήποτε ευαίσθητων δεδομένων στον πελάτη, όπως κωδικούς πρόσβασης χρηστών.
- *Για πόσο καιρό χρειάζεσαι να διατηρήσεις αυτά τα δεδομένα;* Σκοπεύεις ναπροσπελάσεις αυτά τα δεδομένα μόνο για την τρέχουσα συνεδρία ή θέλεις να αποθηκευτούν για πάντα;
- *Είναι τα δεδομένα ευαίσθητα;*Πρέπει να αποφεύγεις την αποθήκευση οποιωνδήποτε ευαίσθητων δεδομένων στον πελάτη, όπως κωδικούς πρόσβασης χρηστών.
- *Για πόσο καιρό χρειάζεσαι να διατηρήσεις αυτά τα δεδομένα;* Σκοπεύεις ναέχεις πρόσβαση σε αυτά τα δεδομένα μόνο για την τρέχουσα συνεδρία ή θέλεις να αποθηκευτούν για πάντα;
Υπάρχουν πολλοί τρόποι αποθήκευσης πληροφοριών μέσα σε μια διαδικτυακή εφαρμογή, ανάλογα με το τι θέλεις να επιτύχεις. Για παράδειγμα, μπορείς να χρησιμοποιήσεις τις διευθύνσεις URL γιανα αποθηκεύσεις ένα ερώτημα αναζήτησης και να το κάνεις κοινόχρηστο μεταξύ χρηστών. Μπορείς επίσης να χρησιμοποιήσεις [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) αν τα δεδομένα χρειάζεται να μοιραστούν με τον διακομιστή, όπως πληροφορίες [αυθεντικοποίησης](https://en.wikipedia.org/wiki/Authentication).
Μια άλλη επιλογή είναι να χρησιμοποιήσεις μία από τις πολλές APIs του προγράμματος περιήγησης για την αποθήκευση δεδομένων. Δύο από αυτές είναι ιδιαίτερα ενδιαφέρουσες:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): ένα [Κατάστημα Κλειδιού/Τιμής](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) που επιτρέπει τη διατήρηση δεδομένων συγκεκριμένων για την τρέχουσα ιστοσελίδα μεταξύ διαφορετικών συνεδριών. Τα δεδομένα που αποθηκεύονται σε αυτό δεν λήγουν ποτέ.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): αυτό λειτουργεί το ίδιο με το `localStorage` εκτός από το ότι τα δεδομένα που αποθηκεύονται σε αυτό διαγράφονται όταν η συνεδρία τελειώσει (όταν κλείσει το πρόγραμμα περιήγησης).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): ένα [Κατάστημα Κλειδιού/Τιμής](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) που επιτρέπει τη διατήρηση δεδομένων συγκεκριμένων για τον τρέχοντα ιστότοπο μεταξύ διαφορετικών συνεδριών. Τα δεδομένα που αποθηκεύονται σε αυτό δεν λήγουν ποτέ.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): λειτουργεί το ίδιο με το `localStorage`, εκτός από το ότι τα δεδομένα που αποθηκεύονται σε αυτό διαγράφονται όταν τελειώνει η συνεδρία (όταν κλείνει το πρόγραμμα περιήγησης).
Σημείωσε ότι και οι δύο αυτές APIs επιτρέπουν μόνο την αποθήκευση [συμβολοσειρών](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Αν θέλεις να αποθηκεύσεις σύνθετα αντικείμενα, θα χρειαστεί να τα σειριοποιήσεις στη μορφή [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) χρησιμοποιώντας το [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Σημείωσε ότι και οι δύο αυτές APIs επιτρέπουν μόνο την αποθήκευση [συμβολοσειρών](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Αν θέλεις να αποθηκεύσεις σύνθετα αντικείμενα, θα χρειαστεί να τα μετατρέψεις σε μορφή [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) χρησιμοποιώντας το [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Αν θέλεις να δημιουργήσεις μια διαδικτυακή εφαρμογή που δεν λειτουργεί με διακομιστή, είναι επίσης δυνατό να δημιουργήσεις μια βάση δεδομένων στον πελάτη χρησιμοποιώντας το [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Αυτό προορίζεται για προχωρημένες περιπτώσεις χρήσης ή αν χρειάζεσαι να αποθηκεύσεις σημαντική ποσότητα δεδομένων, καθώς είναι πιο περίπλοκο στη χρήση.
Με αυτό, τα δεδομένα λογαριασμού χρήστη θα διατηρούνται και θα είναι πάντα ενημερωμένα καθώς κεντροποιήσαμε προηγουμένως όλες τις ενημερώσεις κατάστασης. Εδώ αρχίζουμε να επωφελούμαστε από όλες τις προηγούμενες αναδιαρθρώσεις μας 🙂.
Με αυτό, τα δεδομένα λογαριασμού χρήστη θα διατηρούνται και θα είναι πάντα ενημερωμένα, καθώς προηγουμένως κεντροποιήσαμε όλες τις ενημερώσεις κατάστασης. Εδώ αρχίζουμε να επωφελούμαστε από όλες τις προηγούμενες αναδιαρθρώσεις μας 🙂.
Καθώς τα δεδομένα αποθηκεύονται, πρέπει επίσης να φροντίσουμε να τα επαναφέρουμε όταν φορτώνεται η εφαρμογή. Επειδή θα αρχίσουμε να έχουμε περισσότερο κώδικα αρχικοποίησης, μπορεί να είναι καλή ιδέα να δημιουργήσουμε μια νέα συνάρτηση `init`, που περιλαμβάνει επίσης τον προηγούμενο κώδικα στο κάτω μέρος του `app.js`:
Καθώς τα δεδομένα αποθηκεύονται, πρέπει επίσης να φροντίσουμε για την επαναφορά τους όταν φορτώνεται η εφαρμογή. Δεδομένου ότι θα αρχίσουμε να έχουμε περισσότερο κώδικα αρχικοποίησης, μπορεί να είναι καλή ιδέα να δημιουργήσουμε μια νέα συνάρτηση `init`, η οποία θα περιλαμβάνει και τον προηγούμενο κώδικα στο τέλος του `app.js`:
```js
function init() {
@ -202,16 +202,20 @@ function init() {
init();
```
Εδώ ανακτούμε τα αποθηκευμένα δεδομένα και, αν υπάρχουν, ενημερώνουμε την κατάσταση ανάλογα. Είναι σημαντικό να το κάνουμε *πριν* ενημερώσουμε τη διαδρομή, καθώς μπορεί να υπάρχει κώδικας που βασίζεται στην κατάσταση κατά την ενημέρωση της σελίδας.
Εδώ ανακτούμε τα αποθηκευμένα δεδομένα και, αν υπάρχουν, ενημερώνουμε την κατάσταση ανάλογα. Είναι σημαντικό να το κάνουμε αυτό *πριν* ενημερώσουμε τη διαδρομή, καθώς μπορεί να υπάρχει κώδικας που βασίζεται στην κατάσταση κατά την ενημέρωση της σελίδας.
Μπορούμε επίσης να κάνουμε τη σελίδα *Πίνακας Ελέγχου* την προεπιλεγμένη σελίδα της εφαρμογής μας, καθώς τώρα διατηρούμε τα δεδομένα λογαριασμού. Αν δεν βρεθούν δεδομένα, ο πίνακας ελέγχου φροντίζει να ανακατευθύνει στη σελίδα *Σύνδεση* ούτως ή άλλως. Στην `updateRoute()`, αντικατάστησε την εναλλακτική `return navigate('/login');` με `return navigate('/dashboard
[Υλοποίηση του διαλόγου "Προσθήκη συναλλαγής"](assignment.md)
Μπορούμε επίσης να κάνουμε τη σελίδα *Πίνακας Ελέγχου* την προεπιλεγμένη σελίδα της εφαρμογής μας, καθώς τώρα διατηρούμε τα δεδομένα λογαριασμού. Αν δεν βρεθούν δεδομένα, ο πίνακας ελέγχου φροντίζει να ανακατευθύνει στη σελίδα *Σύνδεση* ούτως ή άλλως. Στη συνάρτηση `updateRoute()`, αντικατάστησε την προεπιλεγμένη `return navigate('/login');` με `
[Κουίζ μετά τη διάλεξη](https://ff-quizzes.netlify.app/web/quiz/48)
## Εργασία
[Υλοποιήστε το παράθυρο διαλόγου "Προσθήκη συναλλαγής"](assignment.md)
Ακολουθεί ένα παράδειγμα αποτελέσματος μετά την ολοκλήρωση της εργασίας:


---
**Αποποίηση Ευθύνης**:
Αυτό το έγγραφο έχει μεταφραστεί χρησιμοποιώντας την υπηρεσία αυτόματης μετάφρασης AI [Co-op Translator](https://github.com/Azure/co-op-translator). Παρόλο που καταβάλλουμε κάθε προσπάθειαγια ακρίβεια, παρακαλούμε να έχετε υπόψη ότι οι αυτόματες μεταφράσεις ενδέχεται να περιέχουν λάθη ή ανακρίβειες. Το πρωτότυπο έγγραφο στη μητρική του γλώσσα θα πρέπει να θεωρείται η αυθεντική πηγή. Για κρίσιμες πληροφορίες, συνιστάται επαγγελματική ανθρώπινη μετάφραση. Δεν φέρουμε ευθύνη για τυχόν παρεξηγήσεις ή εσφαλμένες ερμηνείες που προκύπτουν από τη χρήση αυτής της μετάφρασης.
**Αποποίηση ευθύνης**:
Αυτό το έγγραφο έχει μεταφραστεί χρησιμοποιώντας την υπηρεσία αυτόματης μετάφρασης [Co-op Translator](https://github.com/Azure/co-op-translator). Παρόλο που καταβάλλουμε προσπάθειεςγια ακρίβεια, παρακαλούμε να έχετε υπόψη ότι οι αυτόματες μεταφράσεις ενδέχεται να περιέχουν σφάλματα ή ανακρίβειες. Το πρωτότυπο έγγραφο στη μητρική του γλώσσα θα πρέπει να θεωρείται η αυθεντική πηγή. Για κρίσιμες πληροφορίες, συνιστάται επαγγελματική ανθρώπινη μετάφραση. Δεν φέρουμε ευθύνη για τυχόν παρεξηγήσεις ή εσφαλμένες ερμηνείες που προκύπτουν από τη χρήση αυτής της μετάφρασης.
As a web application grows, it becomes challenging to manage all the data flows. Which code retrieves the data, which page uses it, where and when it needs to be updated... It's easy to end up with messy code that's hard to maintain. This is especially true when you need to share data across different pages of your app, such as user data. The concept of *state management* has always existed in all types of programs, but as web apps grow more complex, it has become a critical aspect to consider during development.
As web applications grow, managing data flows becomes increasingly challenging. Questions like which code retrieves the data, which page uses it, where and when it needs to be updated can lead to messy, hard-to-maintain code. This is especially true when data needs to be shared across different pages, such as user information. The concept of *state management* has always been present in programming, but as web apps become more complex, it has become a critical aspect of development.
In this final part, we'll revisit the app we built to rethink how the state is managed. We'll add support for browser refresh at any point and ensure data persists across user sessions.
In this final part, we'll revisit the app we built to improve state management, enabling browser refresh at any point and persisting data across user sessions.
### Prerequisite
You need to have completed the [data fetching](../3-data/README.md) part of the web app for this lesson. You also need to install [Node.js](https://nodejs.org) and [run the server API](../api/README.md) locally to manage account data.
Before starting this lesson, you should have completed the [data fetching](../3-data/README.md) section of the web app. Additionally, you need to install [Node.js](https://nodejs.org) and [run the server API](../api/README.md) locally to manage account data.
You can test that the server is running properly by executing this command in a terminal:
You can verify that the server is running correctly by executing this command in a terminal:
```sh
curl http://localhost:5000/api
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## Rethink state management
In the [previous lesson](../3-data/README.md), we introduced a basic concept of state in our app with the global `account` variable, which contains the bank data for the currently logged-in user. However, our current implementation has some flaws. Try refreshing the page when you're on the dashboard. What happens?
In the [previous lesson](../3-data/README.md), we introduced a basic concept of state in our app using the global `account` variable, which holds the bank data for the currently logged-in user. However, our current implementation has some shortcomings. Try refreshing the page while on the dashboard. What happens?
There are three issues with the current code:
- The state is not persisted, so a browser refresh takes you back to the login page.
- Multiple functions modify the state. As the app grows, this can make it hard to track changes, and it's easy to forget to update something.
- The state is not cleared, so when you click *Logout*, the account data remains even though you're on the login page.
- The state is not persisted, so refreshing the browser takes you back to the login page.
- Multiple functions modify the state, which can make tracking changes difficult as the app grows. It's easy to forget to update one.
- The state is not cleaned up, meaning that when you click *Logout*, the account data remains even though you're on the login page.
We could update our code to address these issues one by one, but that would lead to more code duplication and make the app harder to maintain. Instead, we can pause for a moment and rethink our strategy.
We could address these issues individually, but doing so would lead to code duplication and make the app harder to maintain. Instead, we can pause and rethink our approach.
> What problems are we really trying to solve here?
[State management](https://en.wikipedia.org/wiki/State_management) is about finding a good approach to solve these two key problems:
[State management](https://en.wikipedia.org/wiki/State_management) is about finding effective ways to address two key challenges:
- How can we keep the data flows in an app easy to understand?
- How can we ensure the state data is always in sync with the user interface (and vice versa)?
- How to keep data flows in an app understandable?
- How to ensure the state data is always synchronized with the user interface (and vice versa)?
Once these are addressed, other issues may either be resolved or become easier to fix. There are many possible approaches to solving these problems, but we'll use a common solution: **centralizing the data and the ways to change it**. The data flows would look like this:
Once these challenges are addressed, other issues may either resolve themselves or become easier to fix. There are many approaches to solving these problems, but we'll use a common solution: **centralizing the data and the methods to modify it**. The data flows will look like this:

> We won't cover the part where data automatically triggers view updates, as that involves more advanced concepts of [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). It's a great follow-up topic if you're interested in diving deeper.
> We won't cover the part where data automatically triggers view updates, as it involves more advanced concepts of [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). It's a great topic for further exploration.
✅ There are many libraries with different approaches to state management, [Redux](https://redux.js.org) being a popular option. Exploring its concepts and patterns can help you understand potential issues in large web apps and how to solve them.
✅ There are many libraries with different approaches to state management, such as [Redux](https://redux.js.org), which is a popular choice. Exploring its concepts and patterns can help you understand potential issues in large web apps and how to address them.
### Task
@ -75,9 +75,9 @@ let state = {
};
```
The idea is to *centralize* all our app data in a single state object. For now, we only have `account` in the state, so this doesn't change much, but it sets the stage for future enhancements.
The idea is to *centralize* all app data in a single state object. For now, we only have `account` in the state, so this change is minimal, but it sets the stage for future enhancements.
We also need to update the functions that use it. In the `register()` and `login()` functions, replace `account = ...` with `state.account = ...`;
Next, update the functions that use it. In the `register()` and `login()` functions, replace `account = ...` with `state.account = ...`;
At the top of the `updateDashboard()` function, add this line:
@ -85,21 +85,21 @@ At the top of the `updateDashboard()` function, add this line:
const account = state.account;
```
This refactoring alone doesn't bring significant improvements, but it lays the groundwork for the next changes.
This refactoring doesn't bring significant improvements yet, but it lays the groundwork for upcoming changes.
## Track data changes
Now that we have the `state` object to store our data, the next step is to centralize updates. This makes it easier to track changes and when they occur.
To prevent direct modifications to the `state` object, it's a good practice to treat it as [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), meaning it cannot be modified directly. Instead, you create a new state object whenever you want to make changes. This approach helps avoid unwanted [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) and opens up possibilities for features like undo/redo, while also making debugging easier. For example, you could log every state change and keep a history to trace the source of a bug.
To prevent direct modifications to the `state` object, it's a good practice to treat it as [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), meaning it cannot be altered. Instead, you create a new state object whenever changes are needed. This approach protects against unwanted [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) and opens up possibilities for features like undo/redo functionality, while also simplifying debugging. For instance, you could log every state change and maintain a history to trace bugs.
In JavaScript, you can use [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to make an object immutable. If you try to modify an immutable object, an exception will be thrown.
In JavaScript, you can use [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to make an object immutable. Attempting to modify an immutable object will raise an exception.
✅ Do you know the difference between a *shallow* and a *deep* immutable object? You can read about it [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Do you know the difference between a *shallow* and a *deep* immutable object? Learn more [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Task
Let's create a new `updateState()` function:
Create a new `updateState()` function:
```js
function updateState(property, newData) {
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
In this function, we create a new state object and copy data from the previous state using the [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Then, we override a specific property of the state object with the new data using the [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties)`[property]` for assignment. Finally, we lock the object to prevent modifications using `Object.freeze()`. For now, we only have the `account` property in the state, but this approach allows you to add more properties as needed.
This function creates a new state object, copies data from the previous state using the [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals), and updates a specific property using the [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties). Finally, it locks the object with `Object.freeze()` to prevent modifications. Currently, the state only contains the `account` property, but this approach allows for adding more properties as needed.
We'll also update the `state` initialization to ensure the initial state is frozen:
Update the `state` initialization to ensure the initial state is frozen:
```js
let state = Object.freeze({
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
Next, update the `register` function by replacing the `state.account = result;` assignment with:
Next, update the `register` function by replacing `state.account = result;` with:
```js
updateState('account', result);
@ -132,9 +132,9 @@ Do the same for the `login` function, replacing `state.account = data;` with:
updateState('account', data);
```
We'll also fix the issue of account data not being cleared when the user clicks *Logout*.
Now, let's address the issue of account data not being cleared when the user clicks *Logout*.
Create a new function `logout()`:
Create a new `logout()` function:
```js
function logout() {
@ -145,47 +145,47 @@ function logout() {
In `updateDashboard()`, replace the redirection `return navigate('/login');` with `return logout()`;
Try registering a new account, logging out, and logging in again to ensure everything works as expected.
Test the app by registering a new account, logging out, and logging back in to ensure everything works correctly.
> Tip: You can track all state changes by adding `console.log(state)` at the bottom of `updateState()` and opening the browser's developer tools console.
> Tip: You can monitor state changes by adding `console.log(state)` at the bottom of `updateState()` and checking the browser's developer console.
## Persist the state
Most web apps need to persist data to function correctly. Critical data is usually stored in a database and accessed via a server API, like the user account data in our case. However, sometimes it's useful to persist data on the client side for a better user experience or improved loading performance.
Most web apps need to persist data for proper functionality. Critical data is typically stored in a database and accessed via a server API, like user account data in our case. However, persisting some data on the client side can enhance user experience and improve loading performance.
When persisting data in the browser, consider these questions:
When storing data in the browser, consider the following:
- *Is the data sensitive?* Avoid storing sensitive data on the client, such as user passwords.
- *How long do you need to keep this data?* Will the data be used only for the current session, or should it be stored indefinitely?
- *Is the data sensitive?* Avoid storing sensitive information like passwords on the client side.
- *How long should the data be kept?* Should it be accessible only during the current session or stored indefinitely?
There are several ways to store information in a web app, depending on your goals. For example, you can use URLs to store a search query and make it shareable. You can also use [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) for data that needs to be shared with the server, such as [authentication](https://en.wikipedia.org/wiki/Authentication) information.
There are various ways to store information in a web app, depending on your goals. For example, you can use URLs to store search queries for sharing between users. You can also use [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) for data that needs to be shared with the server, such as [authentication](https://en.wikipedia.org/wiki/Authentication) details.
Two browser APIs are particularly useful for storing data:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): A [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) that persists data specific to the current website across sessions. The data never expires.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): Similar to `localStorage`, but the data is cleared when the session ends (e.g., when the browser is closed).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): A [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) that persists data specific to the current website across sessions. Data saved here never expires.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): Similar to `localStorage`, but data is cleared when the browser session ends.
Both APIs only allow storing [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). To store complex objects, you need to serialize them to [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format using [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Both APIs only store [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). To store complex objects, you need to serialize them using [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ If you want to create a web app without a server, you can use the [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) to create a client-side database. This is suitable for advanced use cases or when you need to store large amounts of data, though it is more complex to use.
✅ If you want to create a web app without a server, you can use the [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) to create a client-side database. This is suitable for advanced use cases or storing large amounts of data, though it is more complex to use.
### Task
We want users to stay logged in until they explicitly click the *Logout* button, so we'll use `localStorage` to store the account data. First, define a key for storing the data:
We want users to remain logged in until they explicitly click the *Logout* button, so we'll use `localStorage` to store account data. First, define a key for storing the data:
```js
const storageKey = 'savedAccount';
```
Then add this line at the end of the `updateState()` function:
Then, add this line at the end of the `updateState()` function:
This ensures the user account data is persisted and always up-to-date, thanks to our centralized state updates. This is where our earlier refactoring pays off 🙂.
This ensures the user account data is persisted and always up-to-date, thanks to our centralized state updates. This is where the benefits of our earlier refactoring become apparent 🙂.
We also need to restore the data when the app loads. Since we'll have more initialization code, it's a good idea to create a new `init` function, which includes the previous code at the bottom of `app.js`:
Next, restore the saved data when the app loads. Since we'll have more initialization code, create a new `init` function that includes the previous code at the bottom of `app.js`:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Here, we retrieve the saved data and update the state if any is found. It's important to do this*before* updating the route, as some code may rely on the state during page updates.
Here, we retrieve the saved data and update the state if any is found. This must happen*before* updating the route, as some code may rely on the state during page updates.
We can also make the *Dashboard* page the default page of our app, as the account data is now persisted. If no data is found, the dashboard will redirect to the *Login* page. In `updateRoute()`, replace the fallback `return navigate('/login');` with `return navigate('/dashboard');`.
We can also make the *Dashboard* page the default page of the app, as account data is now persisted. If no data is found, the dashboard will redirect to the *Login* page. In `updateRoute()`, replace the fallback `return navigate('/login');` with `return navigate('/dashboard');`.
Now log in to the app and try refreshing the page. You should remain on the dashboard. With this update, we've resolved all the initial issues...
Log in to the app and try refreshing the page. You should remain on the dashboard. With this update, we've resolved all initial issues...
## Refresh the data
...But we may have introduced a new one. Oops!
Go to the dashboard using the `test` account, then run this command in a terminal to create a new transaction:
Log in using the `test` account, then run this command in a terminal to create a new transaction:
Now refresh the dashboard page in the browser. What happens? Do you see the new transaction?
Refresh the dashboard page in your browser. What happens? Do you see the new transaction?
The state is persisted indefinitely thanks to `localStorage`, but this also means it doesn't update until you log out and log back in!
The state is persisted indefinitely via `localStorage`, but it isn't updated until you log out and log back in!
One possible solution is to reload the account data every time the dashboard is loaded to avoid stale data.
One strategy to fix this is to reload the account data whenever the dashboard is loaded, ensuring the data is up-to-date.
### Task
Create a new function `updateAccountData`:
Create a new `updateAccountData` function:
```js
async function updateAccountData() {
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
This function checks if the user is logged in, then reloads the account data from the server.
This function checks if the user is logged in and reloads the account data from the server.
Create another function named `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
This function updates the account data and refreshes the HTML of the dashboard page. It's what we need to call when the dashboard route is loaded. Update the route definition with:
This function updates the account data and refreshes the dashboard's HTML. Call it when the dashboard route is loaded. Update the route definition with:
```js
const routes = {
@ -267,21 +267,21 @@ const routes = {
};
```
Now try reloading the dashboard. It should display the updated account data.
Refresh the dashboard now, and it should display the updated account data.
---
## 🚀 Challenge
Now that we reload the account data every time the dashboard is loaded, do you think we still need to persist *all the account* data?
Now that we reload the account data whenever the dashboard is loaded, do you think we still need to persist *all the account* data?
Work together to modify what is saved and loaded from `localStorage` to include only the data absolutely necessary for the app to function.
Here's an example result after completing the assignment:
@ -291,4 +291,4 @@ Here's an example result after completing the assignment:
---
**Disclaimer**:
This document has been translated using the AI translation service [Co-op Translator](https://github.com/Azure/co-op-translator). While we strive for accuracy, please note that automated translations may contain errors or inaccuracies. The original document in its native language should be regarded as the authoritative source. For critical information, professional human translation is recommended. We are not responsible for any misunderstandings or misinterpretations resulting from the use of this translation.
This document has been translated using the AI translation service [Co-op Translator](https://github.com/Azure/co-op-translator). While we aim for accuracy, please note that automated translations may contain errors or inaccuracies. The original document in its native language should be regarded as the authoritative source. For critical information, professional human translation is recommended. We are not responsible for any misunderstandings or misinterpretations resulting from the use of this translation.
A medida que una aplicación web crece, se vuelve un desafío mantener un seguimiento de todos los flujos de datos. ¿Qué código obtiene los datos, qué página los consume, dónde y cuándo necesitan ser actualizados...? Es fácil terminar con un código desordenado que es difícil de mantener. Esto es especialmente cierto cuando necesitas compartir datos entre diferentes páginas de tu aplicación, por ejemplo, datos de usuario. El concepto de *gestión de estado* siempre ha existido en todo tipo de programas, pero a medida que las aplicaciones web siguen creciendo en complejidad, ahora es un punto clave a considerar durante el desarrollo.
A medida que una aplicación web crece, se vuelve un desafío mantener un seguimiento de todos los flujos de datos. ¿Qué código obtiene los datos, qué página los consume, dónde y cuándo necesitan ser actualizados...? Es fácil terminar con un código desordenado y difícil de mantener. Esto es especialmente cierto cuando necesitas compartir datos entre diferentes páginas de tu aplicación, como los datos del usuario. El concepto de *gestión de estado* siempre ha existido en todo tipo de programas, pero a medida que las aplicaciones web siguen creciendo en complejidad, ahora es un punto clave a considerar durante el desarrollo.
En esta última parte, revisaremos la aplicación que construimos para replantear cómo se gestiona el estado, permitiendo soporte para la actualización del navegador en cualquier momento y persistiendo datos entre sesiones de usuario.
En esta última parte, revisaremos la aplicación que construimos para replantear cómo se gestiona el estado, permitiendo soporte para la actualización del navegador en cualquier momento y la persistencia de datos entre sesiones de usuario.
### Prerrequisitos
Necesitas haber completado la parte de [obtención de datos](../3-data/README.md) de la aplicación web para esta lección. También necesitas instalar [Node.js](https://nodejs.org) y [ejecutar el servidor API](../api/README.md) localmente para poder gestionar los datos de la cuenta.
Necesitas haber completado la parte de [obtención de datos](../3-data/README.md) de la aplicación web para esta lección. También necesitas instalar [Node.js](https://nodejs.org) y [ejecutar la API del servidor](../api/README.md) localmente para poder gestionar los datos de la cuenta.
Puedes probar que el servidor está funcionando correctamente ejecutando este comando en una terminal:
@ -34,13 +34,13 @@ curl http://localhost:5000/api
## Replantear la gestión de estado
En la [lección anterior](../3-data/README.md), introdujimos un concepto básico de estado en nuestra aplicación con la variable global `account`, que contiene los datos bancarios del usuario actualmente conectado. Sin embargo, nuestra implementación actual tiene algunos defectos. Intenta actualizar la página cuando estés en el tablero. ¿Qué sucede?
En la [lección anterior](../3-data/README.md), introdujimos un concepto básico de estado en nuestra aplicación con la variable global `account`, que contiene los datos bancarios del usuario actualmente conectado. Sin embargo, nuestra implementación actual tiene algunos defectos. Intenta actualizar la página cuando estés en el panel de control. ¿Qué sucede?
Hay tres problemas con el código actual:
- El estado no se persiste, ya que al actualizar el navegador vuelves a la página de inicio de sesión.
- Hay múltiples funciones que modifican el estado. A medida que la aplicación crece, puede ser difícil rastrear los cambios y es fácil olvidar actualizar uno.
- El estado no se limpia, por lo que cuando haces clic en *Cerrar sesión*, los datos de la cuenta aún están presentes aunque estés en la página de inicio de sesión.
- Hay múltiples funciones que modifican el estado. A medida que la aplicación crece, puede ser difícil seguir los cambios y es fácil olvidar actualizar uno.
- El estado no se limpia, por lo que cuando haces clic en *Cerrar sesión*, los datos de la cuenta aún están allí, aunque estés en la página de inicio de sesión.
Podríamos actualizar nuestro código para abordar estos problemas uno por uno, pero eso crearía más duplicación de código y haría que la aplicación sea más compleja y difícil de mantener. O podríamos detenernos unos minutos y replantear nuestra estrategia.
@ -49,9 +49,9 @@ Podríamos actualizar nuestro código para abordar estos problemas uno por uno,
[La gestión de estado](https://en.wikipedia.org/wiki/State_management) se trata de encontrar un buen enfoque para resolver estos dos problemas particulares:
- ¿Cómo mantener los flujos de datos en una aplicación comprensibles?
- ¿Cómo mantener los datos de estado siempre sincronizados con la interfaz de usuario (y viceversa)?
- ¿Cómo mantener los datos del estado siempre sincronizados con la interfaz de usuario (y viceversa)?
Una vez que hayas resuelto estos problemas, cualquier otro problema que puedas tener podría ya estar solucionado o haberse vuelto más fácil de resolver. Hay muchas posibles estrategias para resolver estos problemas, pero optaremos por una solución común que consiste en **centralizar los datos y las formas de cambiarlos**. Los flujos de datos serían así:
Una vez que hayas solucionado estos problemas, cualquier otro problema que puedas tener podría ya estar resuelto o haberse vuelto más fácil de solucionar. Hay muchas posibles estrategias para resolver estos problemas, pero optaremos por una solución común que consiste en **centralizar los datos y las formas de cambiarlos**. Los flujos de datos serían así:

@ -67,7 +67,7 @@ Comenzaremos con un poco de refactorización. Reemplaza la declaración de `acco
let account = null;
```
Con:
Por:
```js
let state = {
@ -91,11 +91,11 @@ Esta refactorización por sí sola no trajo muchas mejoras, pero la idea era sen
Ahora que hemos establecido el objeto `state` para almacenar nuestros datos, el siguiente paso es centralizar las actualizaciones. El objetivo es facilitar el seguimiento de cualquier cambio y cuándo ocurren.
Para evitar que se realicen cambios directamente en el objeto `state`, también es una buena práctica considerarlo [*inmutable*](https://en.wikipedia.org/wiki/Immutable_object), lo que significa que no puede modificarse en absoluto. Esto también implica que debes crear un nuevo objeto de estado si deseas cambiar algo en él. Al hacer esto, construyes una protección contra posibles [efectos secundarios no deseados](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) y abres posibilidades para nuevas características en tu aplicación, como implementar deshacer/rehacer, además de facilitar la depuración. Por ejemplo, podrías registrar cada cambio realizado en el estado y mantener un historial de los cambios para entender el origen de un error.
Para evitar que se realicen cambios directamente en el objeto `state`, también es una buena práctica considerarlo [*inmutable*](https://en.wikipedia.org/wiki/Immutable_object), lo que significa que no se puede modificar en absoluto. Esto también implica que debes crear un nuevo objeto de estado si deseas cambiar algo en él. Al hacer esto, construyes una protección contra posibles [efectos secundarios](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) no deseados y abres posibilidades para nuevas características en tu aplicación, como implementar deshacer/rehacer, además de facilitar la depuración. Por ejemplo, podrías registrar cada cambio realizado en el estado y mantener un historial de los cambios para entender el origen de un error.
En JavaScript, puedes usar [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) para crear una versión inmutable de un objeto. Si intentas realizar cambios en un objeto inmutable, se generará una excepción.
✅ ¿Sabes la diferencia entre un objeto inmutable *superficial* y uno *profundo*? Puedes leer sobre ello [aquí](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ ¿Conoces la diferencia entre un objeto inmutable *superficial* y uno *profundo*? Puedes leer sobre ello [aquí](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Tarea
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
En esta función, estamos creando un nuevo objeto de estado y copiando datos del estado anterior utilizando el [*operador de propagación (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Luego sobrescribimos una propiedad particular del objeto de estado con los nuevos datos utilizando la [notación de corchetes](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para la asignación. Finalmente, bloqueamos el objeto para evitar modificaciones usando `Object.freeze()`. Por ahora solo tenemos la propiedad `account` almacenada en el estado, pero con este enfoque puedes agregar tantas propiedades como necesites en el estado.
En esta función, estamos creando un nuevo objeto de estado y copiando datos del estado anterior utilizando el [*operador de propagación (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Luego sobrescribimos una propiedad particular del objeto de estado con los nuevos datos utilizando la [notación de corchetes](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para la asignación. Finalmente, bloqueamos el objeto para evitar modificaciones usando `Object.freeze()`. Por ahora, solo tenemos la propiedad `account` almacenada en el estado, pero con este enfoque puedes agregar tantas propiedades como necesites en el estado.
También actualizaremos la inicialización de `state` para asegurarnos de que el estado inicial también esté congelado:
@ -132,7 +132,7 @@ Haz lo mismo con la función `login`, reemplazando `state.account = data;` con:
updateState('account', data);
```
Ahora aprovecharemos para solucionar el problema de que los datos de la cuenta no se limpian cuando el usuario hace clic en *Cerrar sesión*.
Ahora aprovecharemos para solucionar el problema de que los datos de la cuenta no se eliminan cuando el usuario hace clic en *Cerrar sesión*.
Crea una nueva función `logout()`:
@ -155,15 +155,15 @@ La mayoría de las aplicaciones web necesitan persistir datos para poder funcion
Cuando deseas persistir datos en tu navegador, hay algunas preguntas importantes que deberías hacerte:
- *¿Los datos son sensibles?* Deberías evitar almacenar cualquier dato sensible en el cliente, como contraseñas de usuario.
- *¿Son los datos sensibles?* Deberías evitar almacenar cualquier dato sensible en el cliente, como contraseñas de usuario.
- *¿Por cuánto tiempo necesitas mantener estos datos?* ¿Planeas acceder a estos datos solo durante la sesión actual o quieres que se almacenen para siempre?
Hay múltiples formas de almacenar información dentro de una aplicación web, dependiendo de lo que quieras lograr. Por ejemplo, puedes usar las URLs para almacenar una consulta de búsqueda y hacerla compartible entre usuarios. También puedes usar [cookies HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) si los datos necesitan ser compartidos con el servidor, como información de [autenticación](https://en.wikipedia.org/wiki/Authentication).
Hay múltiples formas de almacenar información dentro de una aplicación web, dependiendo de lo que quieras lograr. Por ejemplo, puedes usar las URLs para almacenar una consulta de búsqueda y hacerla compartible entre usuarios. También puedes usar [cookies HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) si los datos necesitan ser compartidos con el servidor, como la información de [autenticación](https://en.wikipedia.org/wiki/Authentication).
Otra opción es usar una de las muchas APIs del navegador para almacenar datos. Dos de ellas son particularmente interesantes:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): un [almacenamiento de clave/valor](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) que permite persistir datos específicos del sitio web actual entre diferentes sesiones. Los datos guardados en él nunca expiran.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funciona igual que `localStorage` excepto que los datos almacenados en él se eliminan cuando la sesión termina (cuando se cierra el navegador).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funciona igual que `localStorage`, excepto que los datos almacenados en él se eliminan cuando la sesión termina (cuando se cierra el navegador).
Ten en cuenta que ambas APIs solo permiten almacenar [cadenas de texto](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Si deseas almacenar objetos complejos, necesitarás serializarlos al formato [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) usando [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
@ -183,7 +183,7 @@ Luego agrega esta línea al final de la función `updateState()`:
Con esto, los datos de la cuenta del usuario se persistirán y siempre estarán actualizados, ya que previamente centralizamos todas nuestras actualizaciones de estado. Aquí es donde comenzamos a beneficiarnos de todas nuestras refactorizaciones anteriores 🙂.
Con esto, los datos de la cuenta del usuario se persistirán y estarán siempre actualizados, ya que centralizamos previamente todas nuestras actualizaciones de estado. Aquí es donde comenzamos a beneficiarnos de todas nuestras refactorizaciones anteriores 🙂.
Como los datos se guardan, también debemos encargarnos de restaurarlos cuando se cargue la aplicación. Dado que comenzaremos a tener más código de inicialización, puede ser una buena idea crear una nueva función `init`, que también incluya nuestro código anterior al final de `app.js`:
@ -202,17 +202,17 @@ function init() {
init();
```
Aquí recuperamos los datos guardados, y si hay alguno, actualizamos el estado en consecuencia. Es importante hacer esto *antes* de actualizar la ruta, ya que podría haber código que dependa del estado durante la actualización de la página.
Aquí recuperamos los datos guardados y, si hay alguno, actualizamos el estado en consecuencia. Es importante hacer esto *antes* de actualizar la ruta, ya que podría haber código que dependa del estado durante la actualización de la página.
También podemos hacer que la página *Dashboard* sea la página predeterminada de nuestra aplicación, ya que ahora estamos persistiendo los datos de la cuenta. Si no se encuentran datos, el tablero se encarga de redirigir a la página de *Inicio de sesión* de todos modos. En `updateRoute()`, reemplaza el valor predeterminado `return navigate('/login');` con `return navigate('/dashboard');`.
También podemos hacer que la página del *Panel de Control* sea la página predeterminada de nuestra aplicación, ya que ahora estamos persistiendo los datos de la cuenta. Si no se encuentran datos, el panel de control se encarga de redirigir a la página de *Inicio de Sesión* de todos modos. En `updateRoute()`, reemplaza el valor predeterminado `return navigate('/login');` con `return navigate('/dashboard');`.
Ahora inicia sesión en la aplicación e intenta actualizar la página. Deberías permanecer en el tablero. Con esa actualización hemos solucionado todos nuestros problemas iniciales...
Ahora inicia sesión en la aplicación e intenta actualizar la página. Deberías permanecer en el panel de control. Con esa actualización hemos solucionado todos nuestros problemas iniciales...
## Actualizar los datos
...Pero también podríamos haber creado uno nuevo. ¡Ups!
...¡Pero también podríamos haber creado uno nuevo! ¡Ups!
Ve al tablero usando la cuenta `test`, luego ejecuta este comando en una terminal para crear una nueva transacción:
Ve al panel de control usando la cuenta `test`, luego ejecuta este comando en una terminal para crear una nueva transacción:
Ahora intenta actualizar la página del tablero en el navegador. ¿Qué sucede? ¿Ves la nueva transacción?
Ahora intenta actualizar tu página del panel de control en el navegador. ¿Qué sucede? ¿Ves la nueva transacción?
El estado se persiste indefinidamente gracias a `localStorage`, pero eso también significa que nunca se actualiza hasta que cierras sesión en la aplicación e inicias sesión nuevamente.
El estado se persiste indefinidamente gracias a `localStorage`, pero eso también significa que nunca se actualiza hasta que cierras sesión en la aplicación y vuelves a iniciar sesión.
Una posible estrategia para solucionar esto es recargar los datos de la cuenta cada vez que se cargue el tablero, para evitar datos obsoletos.
Una posible estrategia para solucionar esto es recargar los datos de la cuenta cada vez que se cargue el panel de control, para evitar datos obsoletos.
### Tarea
@ -258,7 +258,7 @@ async function refresh() {
}
```
Esta función actualiza los datos de la cuenta y luego se encarga de actualizar el HTML de la página del tablero. Es lo que necesitamos llamar cuando se cargue la ruta del tablero. Actualiza la definición de la ruta con:
Esta función actualiza los datos de la cuenta y luego se encarga de actualizar el HTML de la página del panel de control. Es lo que necesitamos llamar cuando se cargue la ruta del panel de control. Actualiza la definición de la ruta con:
```js
const routes = {
@ -267,21 +267,21 @@ const routes = {
};
```
Ahora intenta actualizar el tablero, debería mostrar los datos actualizados de la cuenta.
Ahora intenta actualizar el panel de control, debería mostrar los datos de la cuenta actualizados.
---
## 🚀 Desafío
Ahora que recargamos los datos de la cuenta cada vez que se carga el tablero, ¿crees que todavía necesitamos persistir *todos los datos de la cuenta*?
Ahora que recargamos los datos de la cuenta cada vez que se carga el panel de control, ¿crees que todavía necesitamos persistir *todos los datos de la cuenta*?
Intenta trabajar en equipo para cambiar lo que se guarda y se carga desde `localStorage` para incluir solo lo que sea absolutamente necesario para que la aplicación funcione.
Intenta trabajar en equipo para cambiar lo que se guarda y se carga desde `localStorage` para incluir solo lo absolutamente necesario para que la aplicación funcione.
## Cuestionario Posterior a la Clase
[Cuestionario posterior a la clase](https://ff-quizzes.netlify.app/web/quiz/48)
## Tarea
[Implementar el cuadro de diálogo "Agregar transacción"](assignment.md)
Aquí tienes un ejemplo del resultado después de completar la tarea:
@ -291,4 +291,4 @@ Aquí tienes un ejemplo del resultado después de completar la tarea:
---
**Descargo de responsabilidad**:
Este documento ha sido traducido utilizando el servicio de traducción automática [Co-op Translator](https://github.com/Azure/co-op-translator). Si bien nos esforzamos por lograr precisión, tenga en cuenta que las traducciones automáticas pueden contener errores o imprecisiones. El documento original en su idioma nativo debe considerarse como la fuente autorizada. Para información crítica, se recomienda una traducción profesional realizada por humanos. No nos hacemos responsables de malentendidos o interpretaciones erróneas que puedan surgir del uso de esta traducción.
Este documento ha sido traducido utilizando el servicio de traducción automática [Co-op Translator](https://github.com/Azure/co-op-translator). Aunque nos esforzamos por garantizar la precisión, tenga en cuenta que las traducciones automatizadas pueden contener errores o imprecisiones. El documento original en su idioma nativo debe considerarse como la fuente autorizada. Para información crítica, se recomienda una traducción profesional realizada por humanos. No nos hacemos responsables de malentendidos o interpretaciones erróneas que puedan surgir del uso de esta traducción.
# ساخت یک اپلیکیشن بانکی بخش ۴: مفاهیم مدیریت وضعیت
# ساخت اپلیکیشن بانکی قسمت ۴: مفاهیم مدیریت وضعیت
## آزمون پیش از درس
@ -15,15 +15,15 @@ CO_OP_TRANSLATOR_METADATA:
### مقدمه
با رشد یک اپلیکیشن وب، پیگیری جریانهای داده به یک چالش تبدیل میشود. کدام کد دادهها را دریافت میکند، کدام صفحه از آن استفاده میکند، کجا و چه زمانی باید بهروزرسانی شود... بهراحتی میتوان به کدی شلوغ و دشوار برای نگهداری رسید. این موضوع بهویژه زمانی صادق است که نیاز به اشتراکگذاری دادهها بین صفحات مختلف اپلیکیشن خود دارید، مثلاً دادههای کاربر. مفهوم *مدیریت وضعیت* همیشه در انواع برنامهها وجود داشته است، اما با افزایش پیچیدگی اپلیکیشنهای وب، اکنون به یک نکته کلیدی در طول توسعه تبدیل شده است.
با رشد یک اپلیکیشن وب، پیگیری جریانهای دادهای دشوار میشود. کدام کد دادهها را دریافت میکند، کدام صفحه آنها را مصرف میکند، کجا و چه زمانی باید بهروزرسانی شوند... بهراحتی ممکن است کدی نامرتب ایجاد شود که نگهداری آن دشوار باشد. این موضوع بهویژه زمانی صادق است که نیاز به اشتراکگذاری دادهها بین صفحات مختلف اپلیکیشن خود دارید، مانند دادههای کاربر. مفهوم *مدیریت وضعیت* همیشه در انواع برنامهها وجود داشته است، اما با افزایش پیچیدگی اپلیکیشنهای وب، اکنون به یک نکته کلیدی در توسعه تبدیل شده است.
در این بخش پایانی، اپلیکیشنی که ساختهایم را بررسی میکنیم تا نحوه مدیریت وضعیت را بازنگری کنیم، بهگونهای که از تازهسازی مرورگر در هر نقطه پشتیبانی کند و دادهها را در طول جلسات کاربر حفظ کند.
در این بخش پایانی، اپلیکیشنی که ساختهایم را بررسی میکنیم تا نحوه مدیریت وضعیت را بازنگری کنیم، بهطوری که از تازهسازی مرورگر در هر نقطه پشتیبانی کند و دادهها را در جلسات کاربری حفظ کند.
### پیشنیاز
برای این درس، باید بخش [دریافت دادهها](../3-data/README.md) از اپلیکیشن وب را تکمیل کرده باشید. همچنین باید [Node.js](https://nodejs.org) را نصب کرده و [سرور API](../api/README.md) را بهصورت محلی اجرا کنید تا بتوانید دادههای حساب را مدیریت کنید.
برای این درس، باید بخش [دریافت دادهها](../3-data/README.md) از اپلیکیشن وب را تکمیل کرده باشید. همچنین باید [Node.js](https://nodejs.org) را نصب کنید و [سرور API](../api/README.md) را بهصورت محلی اجرا کنید تا بتوانید دادههای حساب را مدیریت کنید.
میتوانید با اجرای این دستور در یک ترمینال، بررسی کنید که سرور بهدرستی اجرا میشود:
برای اطمینان از اجرای صحیح سرور، میتوانید این دستور را در ترمینال اجرا کنید:
```sh
curl http://localhost:5000/api
@ -34,34 +34,34 @@ curl http://localhost:5000/api
## بازنگری مدیریت وضعیت
در [درس قبلی](../3-data/README.md)، یک مفهوم ابتدایی از وضعیت را در اپلیکیشن خود با متغیر سراسری `account` معرفی کردیم که دادههای بانکی کاربر واردشده را نگه میدارد. با این حال، پیادهسازی فعلی ما دارای برخی نقصها است. صفحه را در داشبورد تازهسازی کنید. چه اتفاقی میافتد؟
در [درس قبلی](../3-data/README.md)، یک مفهوم ابتدایی از وضعیت را در اپلیکیشن خود با متغیر جهانی `account` معرفی کردیم که دادههای بانکی کاربر وارد شده را ذخیره میکند. با این حال، پیادهسازی فعلی ما دارای برخی نقصها است. صفحه را در داشبورد تازهسازی کنید. چه اتفاقی میافتد؟
سه مشکل در کد فعلی وجود دارد:
کد فعلی ما سه مشکل دارد:
- وضعیت حفظ نمیشود، زیرا تازهسازی مرورگر شما را به صفحه ورود بازمیگرداند.
- چندین تابع وضعیت را تغییر میدهند. با رشد اپلیکیشن، پیگیری این تغییرات دشوار میشود و بهراحتی میتوان بهروزرسانی یکی از آنها را فراموش کرد.
- چندین تابع وضعیت را تغییر میدهند. با رشد اپلیکیشن، این موضوع میتواند پیگیری تغییرات را دشوار کند و بهراحتی ممکن است بهروزرسانی یکی از آنها فراموش شود.
- وضعیت پاک نمیشود، بنابراین وقتی روی *خروج* کلیک میکنید، دادههای حساب همچنان وجود دارند، حتی اگر در صفحه ورود باشید.
میتوانیم کد خود را بهگونهای بهروزرسانی کنیم که این مشکلات را یکییکی حل کنیم، اما این کار باعث تکرار کد بیشتر و پیچیدهتر شدن اپلیکیشن میشود. یا میتوانیم چند دقیقه مکث کنیم و استراتژی خود را بازنگری کنیم.
میتوانیم کد خود را بهگونهای بهروزرسانی کنیم که این مشکلات را یکییکی حل کنیم، اما این کار باعث ایجاد تکرار کد بیشتر و پیچیدهتر شدن اپلیکیشن میشود. یا میتوانیم چند دقیقه مکث کنیم و استراتژی خود را بازنگری کنیم.
> واقعاً چه مشکلاتی را میخواهیم اینجا حل کنیم؟
> واقعاً چه مشکلاتی را میخواهیم حل کنیم؟
[مدیریت وضعیت](https://en.wikipedia.org/wiki/State_management) تماماً درباره یافتن یک رویکرد مناسب برای حل این دو مشکل خاص است:
[مدیریت وضعیت](https://en.wikipedia.org/wiki/State_management) به یافتن یک رویکرد مناسب برای حل این دو مشکل خاص مربوط میشود:
- چگونه میتوان جریانهای داده در یک اپلیکیشن را قابلفهم نگه داشت؟
- چگونه میتوان دادههای وضعیت را همیشه با رابط کاربری هماهنگ نگه داشت (و بالعکس)؟
- چگونه جریانهای دادهای در یک اپلیکیشن قابل فهم نگه داشته شوند؟
وقتی این موارد را حل کردید، هر مشکل دیگری که ممکن است داشته باشید یا قبلاً حل شده است یا حل آن آسانتر شده است. روشهای زیادی برای حل این مشکلات وجود دارد، اما ما با یک راهحل رایج پیش میرویم که شامل **متمرکز کردن دادهها و روشهای تغییر آنها** است. جریانهای داده به این صورت خواهند بود:
پس از رسیدگی به این موارد، هر مشکل دیگری که ممکن است داشته باشید یا قبلاً حل شده است یا آسانتر قابل حل خواهد بود. روشهای زیادی برای حل این مشکلات وجود دارد، اما ما از یک راهحل رایج استفاده میکنیم که شامل **متمرکز کردن دادهها و روشهای تغییر آنها** است. جریانهای دادهای به این صورت خواهند بود:


> در اینجا بخشی که دادهها بهطور خودکار نمای را بهروزرسانی میکنند پوشش داده نمیشود، زیرا این موضوع به مفاهیم پیشرفتهتر [برنامهنویسی واکنشی](https://en.wikipedia.org/wiki/Reactive_programming) مرتبط است. اگر به دنبال یک مطالعه عمیق هستید، این موضوع میتواند یک موضوع پیگیری خوب باشد.
> در اینجا بخشی که دادهها بهطور خودکار بهروزرسانی نمای را تحریک میکنند، پوشش داده نمیشود، زیرا به مفاهیم پیشرفتهتر [برنامهنویسی واکنشی](https://en.wikipedia.org/wiki/Reactive_programming) مرتبط است. این موضوع میتواند یک موضوع پیگیری خوب باشد اگر به دنبال یک بررسی عمیق هستید.
✅ کتابخانههای زیادی با رویکردهای مختلف برای مدیریت وضعیت وجود دارند، [Redux](https://redux.js.org) یکی از گزینههای محبوب است. به مفاهیم و الگوهای استفادهشده نگاهی بیندازید، زیرا اغلب راه خوبی برای یادگیری مشکلات احتمالی است که ممکن است در اپلیکیشنهای وب بزرگ با آنها مواجه شوید و نحوه حل آنها.
✅ کتابخانههای زیادی با رویکردهای مختلف برای مدیریت وضعیت وجود دارند، [Redux](https://redux.js.org) یکی از گزینههای محبوب است. نگاهی به مفاهیم و الگوهای استفاده شده بیندازید، زیرا اغلب راه خوبی برای یادگیری مشکلات احتمالی در اپلیکیشنهای وب بزرگ و نحوه حل آنها است.
### وظیفه
با کمی بازسازی کد شروع میکنیم. اعلان `account` را جایگزین کنید:
با کمی بازسازی شروع میکنیم. اعلان `account` را جایگزین کنید:
```js
let account = null;
@ -75,7 +75,7 @@ let state = {
};
```
ایده این است که *تمام دادههای اپلیکیشن خود را در یک شیء وضعیت واحد متمرکز کنیم*. در حال حاضر فقط `account`را در وضعیت داریم، بنابراین تغییر زیادی ایجاد نمیکند، اما مسیری برای تکامل ایجاد میکند.
ایده این است که *تمام دادههای اپلیکیشن* خود را در یک شیء وضعیت واحد متمرکز کنیم. در حال حاضر فقط `account` در وضعیت داریم، بنابراین تغییر زیادی ایجاد نمیشود، اما راهی برای تکامل ایجاد میکند.
همچنین باید توابعی که از آن استفاده میکنند را بهروزرسانی کنیم. در توابع `register()` و `login()`، `account = ...` را با `state.account = ...` جایگزین کنید.
@ -85,21 +85,21 @@ let state = {
const account = state.account;
```
این بازسازی بهتنهایی بهبود زیادی ایجاد نکرد، اما ایده این بود که پایهای برای تغییرات بعدی ایجاد کنیم.
این بازسازی بهتنهایی بهبود زیادی ایجاد نکرد، اما ایده این بود که پایهای برای تغییرات بعدی ایجاد شود.
## پیگیری تغییرات دادهها
اکنون که شیء `state` را برای ذخیره دادههای خود ایجاد کردهایم، گام بعدی متمرکز کردن بهروزرسانیها است. هدف این است که پیگیری هرگونه تغییر و زمان وقوع آنها آسانتر شود.
برای جلوگیری از ایجاد تغییرات در شیء `state`، همچنین یک تمرین خوب است که آن را [*غیرقابلتغییر*](https://en.wikipedia.org/wiki/Immutable_object) در نظر بگیریم، به این معنی که اصلاً نمیتوان آن را تغییر داد. این همچنین به این معنی است که اگر میخواهید چیزی را در آن تغییر دهید، باید یک شیء وضعیت جدید ایجاد کنید. با انجام این کار، از اثرات جانبی ناخواسته [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) محافظت میکنید و امکان ایجاد ویژگیهای جدید در اپلیکیشن خود مانند پیادهسازی undo/redo را فراهم میکنید، در حالی که اشکالزدایی را نیز آسانتر میکنید. برای مثال، میتوانید هر تغییری که در وضعیت ایجاد میشود را ثبت کنید و تاریخچه تغییرات را نگه دارید تا منبع یک باگ را درک کنید.
برای جلوگیری از ایجاد تغییرات در شیء `state`، همچنین یک تمرین خوب است که آن را [*غیرقابلتغییر*](https://en.wikipedia.org/wiki/Immutable_object) در نظر بگیریم، به این معنی که اصلاً نمیتوان آن را تغییر داد. این همچنین به این معنی است که اگر بخواهید چیزی را در آن تغییر دهید، باید یک شیء وضعیت جدید ایجاد کنید. با انجام این کار، از اثرات جانبی [ناخواسته](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) محافظت میکنید و امکان ایجاد ویژگیهای جدید در اپلیکیشن خود مانند پیادهسازی undo/redo را فراهم میکنید، در حالی که اشکالزدایی را نیز آسانتر میکنید. برای مثال، میتوانید هر تغییر ایجاد شده در وضعیت را ثبت کنید و تاریخچه تغییرات را نگه دارید تا منبع یک اشکال را درک کنید.
در جاوااسکریپت، میتوانید از [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) برای ایجاد نسخهای غیرقابلتغییر از یک شیء استفاده کنید. اگر سعی کنید تغییراتی در یک شیء غیرقابلتغییر ایجاد کنید، یک استثنا ایجاد میشود.
در جاوااسکریپت، میتوانید از [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) برای ایجاد نسخه غیرقابل تغییر یک شیء استفاده کنید. اگر سعی کنید تغییراتی در یک شیء غیرقابل تغییر ایجاد کنید، یک استثنا ایجاد خواهد شد.
✅ آیا تفاوت بین یک شیء غیرقابلتغییر *سطحی* و *عمیق* را میدانید؟ میتوانید درباره آن [اینجا](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) بخوانید.
✅ آیا تفاوت بین یک شیء غیرقابلتغییر *سطحی* و *عمیق* را میدانید؟ میتوانید درباره آن [اینجا](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) بخوانید.
### وظیفه
یک تابع جدید به نام `updateState()` ایجاد کنید:
بیایید یک تابع جدید `updateState()` ایجاد کنیم:
```js
function updateState(property, newData) {
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
در این تابع، یک شیء وضعیت جدید ایجاد میکنیم و دادهها را از وضعیت قبلی با استفاده از [*عملگر گسترش (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) کپی میکنیم. سپس یک ویژگی خاص از شیء وضعیت را با دادههای جدید با استفاده از [نشانهگذاری براکت](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` برای تخصیص بازنویسی میکنیم. در نهایت، شیء را قفل میکنیم تا از تغییرات جلوگیری کنیم با استفاده از `Object.freeze()`. در حال حاضر فقط ویژگی `account`را در وضعیت ذخیره کردهایم، اما با این رویکرد میتوانید به تعداد مورد نیاز ویژگی به وضعیت اضافه کنید.
در این تابع، یک شیء وضعیت جدید ایجاد میکنیم و دادهها را از وضعیت قبلی با استفاده از [عملگر *گسترش (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) کپی میکنیم. سپس یک ویژگی خاص از شیء وضعیت را با دادههای جدید با استفاده از [نشانهگذاری براکت](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` برای تخصیص بازنویسی میکنیم. در نهایت، شیء را قفل میکنیم تا از تغییرات جلوگیری کنیم با استفاده از `Object.freeze()`. در حال حاضر فقط ویژگی `account`در وضعیت ذخیره شده است، اما با این رویکرد میتوانید به تعداد مورد نیاز ویژگیها را به وضعیت اضافه کنید.
همچنین مقداردهی اولیه `state` را بهروزرسانی میکنیم تا مطمئن شویم وضعیت اولیه نیز قفل شده است:
@ -120,21 +120,21 @@ let state = Object.freeze({
});
```
سپس تابع `register` را بهروزرسانی کنید و تخصیص `state.account = result;` را با این جایگزین کنید:
پس از آن، تابع `register` را با جایگزینی تخصیص `state.account = result;` بهروزرسانی کنید:
```js
updateState('account', result);
```
همین کار را با تابع `login` انجام دهید و تخصیص `state.account = data;` را با این جایگزین کنید:
همین کار را با تابع `login` انجام دهید، تخصیص `state.account = data;` را با:
```js
updateState('account', data);
```
اکنون از این فرصت استفاده میکنیم تا مشکل پاک نشدن دادههای حساب هنگام کلیک کاربر روی *خروج* را برطرف کنیم.
اکنون فرصت را غنیمت میشماریم تا مشکل پاک نشدن دادههای حساب هنگام کلیک کاربر روی *خروج* را حل کنیم.
یک تابع جدید به نام `logout()` ایجاد کنید:
یک تابع جدید `logout()` ایجاد کنید:
```js
function logout() {
@ -143,35 +143,35 @@ function logout() {
}
```
در `updateDashboard()`، تغییر مسیر`return navigate('/login');` را با `return logout();` جایگزین کنید.
در `updateDashboard()`، بازگردانی`return navigate('/login');` را با `return logout();` جایگزین کنید.
یک حساب جدید ثبت کنید، خارج شوید و دوباره وارد شوید تا بررسی کنید که همه چیز همچنان بهدرستی کار میکند.
یک حساب جدید ثبت کنید، خارج شوید و دوباره وارد شوید تا مطمئن شوید که همه چیز هنوز بهدرستی کار میکند.
> نکته: میتوانید با افزودن `console.log(state)` در انتهای `updateState()` و باز کردن کنسول در ابزارهای توسعه مرورگر خود، به تمام تغییرات وضعیت نگاهی بیندازید.
> نکته: میتوانید با افزودن `console.log(state)` در انتهای `updateState()` و باز کردن کنسول در ابزارهای توسعه مرورگر خود، تمام تغییرات وضعیت را مشاهده کنید.
## حفظ وضعیت
بیشتر اپلیکیشنهای وب برای عملکرد صحیح نیاز به حفظ دادهها دارند. تمام دادههای حیاتی معمولاً در یک پایگاه داده ذخیره میشوند و از طریق یک سرور API دسترسی پیدا میکنند، مانند دادههای حساب کاربر در مورد ما. اما گاهی اوقات، حفظ برخی دادهها در اپلیکیشن کلاینت که در مرورگر شما اجرا میشود نیز جالب است، برای تجربه کاربری بهتر یا بهبود عملکرد بارگذاری.
بیشتر اپلیکیشنهای وب برای عملکرد صحیح نیاز به حفظ دادهها دارند. تمام دادههای حیاتی معمولاً در یک پایگاه داده ذخیره میشوند و از طریق یک سرور API دسترسی پیدا میکنند، مانند دادههای حساب کاربری در مورد ما. اما گاهی اوقات، حفظ برخی دادهها در اپلیکیشن کلاینت که در مرورگر شما اجرا میشود نیز جالب است، برای تجربه کاربری بهتر یا بهبود عملکرد بارگذاری.
هنگامی که میخواهید دادهها را در مرورگر خود حفظ کنید، چند سؤال مهم وجود دارد که باید از خود بپرسید:
هنگامی که میخواهید دادهها را در مرورگر خود ذخیره کنید، چند سؤال مهم وجود دارد که باید از خود بپرسید:
- *آیا دادهها حساس هستند؟* باید از ذخیره هرگونه داده حساس در کلاینت، مانند رمز عبور کاربر، خودداری کنید.
- *برای چه مدت به این دادهها نیاز دارید؟* آیا قصد دارید فقط برای جلسه فعلی به این دادهها دسترسی داشته باشید یا میخواهید برای همیشه ذخیره شوند؟
- *آیا دادهها حساس هستند؟* باید از ذخیره هرگونه داده حساس در کلاینت، مانند رمزهای عبور کاربر، خودداری کنید.
- *برای چه مدت نیاز دارید این دادهها را نگه دارید؟* آیا قصد دارید فقط برای جلسه فعلی به این دادهها دسترسی داشته باشید یا میخواهید آنها را برای همیشه ذخیره کنید؟
روشهای متعددی برای ذخیره اطلاعات در یک اپلیکیشن وب وجود دارد، بسته به آنچه میخواهید به دست آورید. برای مثال، میتوانید از URLها برای ذخیره یک جستجو استفاده کنید و آن را بین کاربران به اشتراک بگذارید. همچنین میتوانید از [کوکیهای HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) استفاده کنید اگر دادهها نیاز به اشتراک با سرور دارند، مانند اطلاعات [احراز هویت](https://en.wikipedia.org/wiki/Authentication).
روشهای مختلفی برای ذخیره اطلاعات در یک اپلیکیشن وب وجود دارد، بسته به آنچه میخواهید به دست آورید. برای مثال، میتوانید از URLها برای ذخیره یک پرسش جستجو استفاده کنید و آن را بین کاربران به اشتراک بگذارید. همچنین میتوانید از [کوکیهای HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) استفاده کنید اگر دادهها نیاز به اشتراکگذاری با سرور دارند، مانند اطلاعات [احراز هویت](https://en.wikipedia.org/wiki/Authentication).
گزینه دیگر استفاده از یکی از بسیاری از APIهای مرورگر برای ذخیره دادهها است. دو مورد از آنها بهویژه جالب هستند:
گزینه دیگر استفاده از یکی از بسیاری از APIهای مرورگر برای ذخیره دادهها است. دو مورد از آنها بهویژه جالب هستند:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): یک [فروشگاه کلید/مقدار](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) که امکان حفظ دادههای خاص وبسایت فعلی را در جلسات مختلف فراهم میکند. دادههای ذخیرهشده در آن هرگز منقضی نمیشوند.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): این یکی همانند `localStorage` کار میکند، با این تفاوت که دادههای ذخیرهشده در آن هنگام پایان جلسه (بسته شدن مرورگر) پاک میشوند.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): یک [ذخیرهسازی کلید/مقدار](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) که امکان حفظ دادههای خاص وبسایت فعلی را در جلسات مختلف فراهم میکند. دادههای ذخیرهشده در آن هرگز منقضی نمیشوند.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): این مورد همانند `localStorage` عمل میکند، به جز اینکه دادههای ذخیره شده در آن هنگام پایان جلسه (زمان بسته شدن مرورگر) پاک میشوند.
توجه داشته باشید که هر دوی این APIها فقط اجازه ذخیره [رشتهها](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) را میدهند. اگر میخواهید اشیاء پیچیده را ذخیره کنید، باید آن را به فرمت [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) با استفاده از [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) سریالسازی کنید.
توجه داشته باشید که هر دو این APIها فقط اجازه ذخیره [رشتهها](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) را میدهند. اگر میخواهید اشیاء پیچیده ذخیره کنید، باید آنها را به فرمت [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) سریالسازی کنید با استفاده از [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ اگر میخواهید یک اپلیکیشن وب ایجاد کنید که با سرور کار نمیکند، همچنین میتوانید با استفاده از API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) یک پایگاه داده در کلاینت ایجاد کنید. این یکی برای موارد استفاده پیشرفته یا اگر نیاز به ذخیره مقدار زیادی داده دارید، رزرو شده است، زیرا استفاده از آن پیچیدهتر است.
✅ اگر میخواهید یک اپلیکیشن وب ایجاد کنید که با سرور کار نمیکند، همچنین امکان ایجاد یک پایگاه داده در کلاینت با استفاده از API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) وجود دارد. این مورد برای موارد استفاده پیشرفته یا اگر نیاز به ذخیره مقدار قابل توجهی داده دارید، رزرو شده است، زیرا استفاده از آن پیچیدهتر است.
### وظیفه
ما میخواهیم کاربران ما تا زمانی که بهصراحت روی دکمه *خروج* کلیک نکنند، وارد سیستم بمانند، بنابراین از `localStorage` برای ذخیره دادههای حساب استفاده خواهیم کرد. ابتدا، یک کلید تعریف کنید که برای ذخیره دادههای خود از آن استفاده خواهیم کرد:
ما میخواهیم کاربران ما تا زمانی که بهطور صریح روی دکمه *خروج* کلیک نکنند، وارد سیستم باقی بمانند، بنابراین از `localStorage` برای ذخیره دادههای حساب استفاده خواهیم کرد. ابتدا، یک کلید تعریف کنیم که برای ذخیره دادههای خود استفاده خواهیم کرد.
با این کار، دادههای حساب کاربر حفظ میشوند و همیشه بهروز خواهند بود، زیرا قبلاً تمام بهروزرسانیهای وضعیت خود را متمرکز کردهایم. اینجاست که شروع به بهرهبرداری از تمام بازسازیهای قبلی خود میکنیم 🙂.
با این کار، دادههای حساب کاربری حفظ میشوند و همیشه بهروز خواهند بود، همانطور که قبلاً تمام بهروزرسانیهای وضعیت خود را متمرکز کردهایم. اینجاست که شروع به بهرهبرداری از تمام بازسازیهای قبلی خود میکنیم 🙂.
از آنجا که دادهها ذخیره میشوند، همچنین باید هنگام بارگذاری اپلیکیشن آنها را بازیابی کنیم. از آنجا که شروع به داشتن کد مقداردهی اولیه بیشتری میکنیم، ممکن است ایده خوبی باشد که یک تابع جدید `init` ایجاد کنیم، که شامل کد قبلی ما در انتهای `app.js` نیز باشد:
از آنجا که دادهها ذخیره شدهاند، همچنین باید مراقب بازیابی آنها هنگام بارگذاری اپلیکیشن باشیم. از آنجا که شروع به داشتن کد مقداردهی اولیه بیشتری میکنیم، ممکن است ایده خوبی باشد که یک تابع جدید `init` ایجاد کنیم، که شامل کد قبلی ما در انتهای `app.js` نیز باشد:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
در اینجا دادههای ذخیرهشده را بازیابی میکنیم، و اگر دادهای وجود داشته باشد، وضعیت را مطابق آن بهروزرسانی میکنیم. مهم است که این کار را *قبل از* بهروزرسانی مسیر انجام دهیم، زیرا ممکن است کدی وجود داشته باشد که در طول بهروزرسانی صفحه به وضعیت متکی باشد.
در اینجا دادههای ذخیرهشده را بازیابی میکنیم، و اگر دادهای وجود داشته باشد، وضعیت را بهطور مناسب بهروزرسانی میکنیم. مهم است که این کار را *قبل از* بهروزرسانی مسیر انجام دهیم، زیرا ممکن است کدی وجود داشته باشد که در طول بهروزرسانی صفحه به وضعیت وابسته باشد.
همچنین میتوانیم صفحه *داشبورد* را بهعنوان صفحه پیشفرض اپلیکیشن خود قرار دهیم، زیرا اکنون دادههای حساب را حفظ میکنیم. اگر دادهای یافت نشود، داشبورد به هر حال مسئول تغییر مسیر به صفحه *ورود* است. در `updateRoute()`، بازگشت پیشفرض `return navigate('/login');` را با `return navigate('/dashboard');` جایگزین کنید.
همچنین میتوانیم صفحه *داشبورد* را بهعنوان صفحه پیشفرض اپلیکیشن خود قرار دهیم، زیرا اکنون دادههای حساب را حفظ میکنیم. اگر دادهای یافت نشود، داشبورد به هر حال مسئولیت هدایت به صفحه *ورود* را بر عهده دارد. در `updateRoute()`، بازگردانی پیشفرض `return navigate('/login');` را با `return navigate('/dashboard');` جایگزین کنید.
اکنون وارد اپلیکیشن شوید و سعی کنید صفحه را تازهسازی کنید. باید در داشبورد بمانید. با این بهروزرسانی، تمام مشکلات اولیه خود را برطرف کردهایم...
اکنون وارد اپلیکیشن شوید و صفحه را تازهسازی کنید. باید در داشبورد باقی بمانید. با این بهروزرسانی، تمام مشکلات اولیه خود را حل کردهایم...
## تازهسازی دادهها
## بهروزرسانی دادهها
...اما ممکن است یک مشکل جدید ایجاد کرده باشیم. اوه!
به داشبورد بروید و از حساب `test` استفاده کنید، سپس این دستور را در یک ترمینال اجرا کنید تا یک تراکنش جدید ایجاد کنید:
با حساب `test` به داشبورد بروید، سپس این دستور را در ترمینال اجرا کنید تا یک تراکنش جدید ایجاد کنید:
اکنون سعی کنید صفحه داشبورد را در مرورگر تازهسازی کنید. چه اتفاقی میافتد؟ آیا تراکنش جدید را میبینید؟
اکنون صفحه داشبورد خود را در مرورگر تازهسازی کنید. چه اتفاقی میافتد؟ آیا تراکنش جدید را میبینید؟
وضعیت به لطف `localStorage` بهطور نامحدود حفظ میشود، اما این همچنین به این معنی است که تا زمانی که از اپلیکیشن خارج نشوید و دوباره وارد نشوید، هرگز بهروزرسانی نمیشود!
وضعیت به لطف `localStorage` بهطور نامحدود حفظ میشود، اما این همچنین به این معنی است که تا زمانی که از اپلیکیشن خارج نشوید و دوباره وارد شوید، هرگز بهروزرسانی نمیشود!
یک استراتژی ممکن برای رفع این مشکل این است که هر بار که داشبورد بارگذاری میشود، دادههای حساب را دوباره بارگذاری کنیم تا از دادههای قدیمی جلوگیری کنیم.
یک استراتژی ممکن برای رفع این مشکل این است که دادههای حساب را هر بار که داشبورد بارگذاری میشود، دوباره بارگذاری کنیم تا از دادههای قدیمی جلوگیری کنیم.
### وظیفه
یک تابع جدید به نام `updateAccountData` ایجاد کنید:
یک تابع جدید `updateAccountData` ایجاد کنید:
```js
async function updateAccountData() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
این یکی دادههای حساب را بهروزرسانی میکند، سپس HTML صفحه داشبورد را بهروزرسانی میکند. این همان چیزی است که باید هنگام بارگذاری مسیر داشبورد فراخوانی کنیم. تعریف مسیر را با این بهروزرسانی کنید:
این تابع دادههای حساب را بهروزرسانی میکند، سپس HTML صفحه داشبورد را بهروزرسانی میکند. این همان چیزی است که باید هنگام بارگذاری مسیر داشبورد فراخوانی شود. تعریف مسیر را با این مورد بهروزرسانی کنید:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
اکنون سعی کنید داشبورد را تازهسازی کنید، باید دادههای حساب بهروزرسانیشده را نمایش دهد.
اکنون داشبورد را تازهسازی کنید، باید دادههای حساب بهروزرسانیشده را نمایش دهد.
---
## 🚀 چالش
اکنون که هر بار که داشبورد بارگذاری میشود دادههای حساب را دوباره بارگذاری میکنیم، آیا فکر میکنید هنوز نیاز داریم *تمام دادههای حساب* را حفظ کنیم؟
اکنون که دادههای حساب را هر بار که داشبورد بارگذاری میشود دوباره بارگذاری میکنیم، آیا فکر میکنید هنوز نیاز داریم *تمام دادههای حساب* را حفظ کنیم؟
سعی کنید با هم کار کنید تا آنچه را که از `localStorage` ذخیره و بارگذاری میشود تغییر دهید تا فقط شامل آنچه که برای عملکرد اپلیکیشن کاملاً ضروری است باشد.
سعی کنید با هم کار کنید تا آنچه از `localStorage` ذخیره و بارگذاری میشود را تغییر دهید تا فقط شامل مواردی باشد که برای عملکرد اپلیکیشن کاملاً ضروری هستند.
## آزمون پس از درس
[آزمون پس از درس](https://ff-quizzes.netlify.app/web/quiz/48)
[آزمون پس از سخنرانی](https://ff-quizzes.netlify.app/web/quiz/48)
## تکلیف
[پیادهسازی "افزودن تراکنش" در دیالوگ](assignment.md)
در اینجا یک نمونه نتیجه پس از تکمیل وظیفه آورده شده است:
در اینجا یک نمونه نتیجه پس از تکمیل تکلیف آورده شده است:

---
**سلب مسئولیت**:
این سند با استفاده از سرویس ترجمه هوش مصنوعی [Co-op Translator](https://github.com/Azure/co-op-translator) ترجمه شده است. در حالی که ما برای دقت تلاش میکنیم، لطفاً توجه داشته باشید که ترجمههای خودکار ممکن است شامل خطاها یا نادرستیهایی باشند. سند اصلی به زبان اصلی آن باید به عنوان منبع معتبر در نظر گرفته شود. برای اطلاعات حساس، ترجمه حرفهای انسانی توصیه میشود. ما هیچ مسئولیتی در قبال سوءتفاهمها یا تفسیرهای نادرست ناشی از استفاده از این ترجمه نداریم.
این سند با استفاده از سرویس ترجمه هوش مصنوعی [Co-op Translator](https://github.com/Azure/co-op-translator) ترجمه شده است. در حالی که ما برای دقت تلاش میکنیم، لطفاً توجه داشته باشید که ترجمههای خودکار ممکن است حاوی خطاها یا نادرستیهایی باشند. سند اصلی به زبان اصلی آن باید به عنوان منبع معتبر در نظر گرفته شود. برای اطلاعات حیاتی، ترجمه حرفهای انسانی توصیه میشود. ما مسئولیتی در قبال هرگونه سوءتفاهم یا تفسیر نادرست ناشی از استفاده از این ترجمه نداریم.
Kun verkkosovellus kasvaa, datavirtojen hallinta muuttuu haastavaksi. Mikä koodi hakee tiedot, mikä sivu käyttää niitä, missä ja milloin niitä pitää päivittää... on helppo päätyä sekavaan koodiin, jota on vaikea ylläpitää. Tämä on erityisen totta, kun tietoja täytyy jakaa sovelluksen eri sivujen välillä, esimerkiksi käyttäjätietoja. *Tilanhallinnan* käsite on aina ollut olemassa kaikenlaisissa ohjelmissa, mutta verkkosovellusten monimutkaistuessa siitä on tullut kehityksen keskeinen osa.
Kun verkkosovellus kasvaa, datavirtojen hallinnasta tulee haastavaa. Mikä koodi hakee tiedot, mikä sivu käyttää niitä, missä ja milloin niitä pitää päivittää... On helppo päätyä sekavaan koodiin, jota on vaikea ylläpitää. Tämä on erityisen totta, kun tietoja pitää jakaa sovelluksen eri sivujen välillä, esimerkiksi käyttäjätietoja. *Tilanhallinnan* käsite on aina ollut olemassa kaikenlaisissa ohjelmissa, mutta verkkosovellusten monimutkaistuessa siitä on tullut kehityksen keskeinen osa.
Tässä viimeisessä osassa tarkastelemme rakentamaamme sovellusta uudelleen ja mietimme, miten tila hallitaan paremmin. Näin voimme tukea selaimen päivityksiä missä tahansa vaiheessa ja säilyttää tiedot käyttäjäistuntojen välillä.
Tässä viimeisessä osassa tarkastelemme rakentamaamme sovellusta uudelleen ja mietimme, miten tilaa hallitaan, jotta voimme tukea selaimen päivityksiä missä tahansa vaiheessa ja säilyttää tiedot käyttäjäistuntojen välillä.
### Esitiedot
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## Tilanhallinnan uudelleenarviointi
[Edellisessä oppitunnissa](../3-data/README.md) esittelimme sovelluksessamme tilan peruskäsitteen käyttämällä globaalia `account`-muuttujaa, joka sisältää kirjautuneen käyttäjän pankkitiedot. Nykyisessä toteutuksessamme on kuitenkin joitakin puutteita. Kokeile päivittää sivu, kun olet hallintapaneelissa. Mitä tapahtuu?
Edellisessä [oppitunnissa](../3-data/README.md) esittelimme sovelluksessamme tilan peruskäsitteen globaalin `account`-muuttujan avulla, joka sisältää kirjautuneen käyttäjän pankkitiedot. Nykyisessä toteutuksessamme on kuitenkin joitakin puutteita. Kokeile päivittää sivu, kun olet hallintapaneelissa. Mitä tapahtuu?
Nykyisessä koodissa on kolme ongelmaa:
- Tila ei säily, sillä selaimen päivitys vie sinut takaisin kirjautumissivulle.
- Useat funktiot muuttavat tilaa. Sovelluksen kasvaessa tämä voi tehdä muutosten seuraamisesta vaikeaa, ja on helppo unohtaa päivittää jokin osa.
- Tila ei tyhjene, joten kun napsautat *Kirjaudu ulos*, tilitiedot ovat yhä olemassa, vaikka olet kirjautumissivulla.
- On useita funktioita, jotka muuttavat tilaa. Sovelluksen kasvaessa tämä voi tehdä muutosten seuraamisesta vaikeaa, ja päivityksiä voi helposti unohtua.
- Tila ei tyhjene, joten kun napsautat *Kirjaudu ulos*, tilitiedot ovat edelleen olemassa, vaikka olet kirjautumissivulla.
Voisimme päivittää koodiamme ratkaistaksemme nämä ongelmat yksi kerrallaan, mutta se lisäisi koodin toistoa ja tekisi sovelluksesta monimutkaisemman ja vaikeamman ylläpitää. Tai voisimme pysähtyä hetkeksi ja miettiä strategiaamme uudelleen.
Voisimme päivittää koodiamme ratkaistaksemme nämä ongelmat yksi kerrallaan, mutta se lisäisi koodin toistoa ja tekisi sovelluksesta monimutkaisemman ja vaikeammin ylläpidettävän. Tai voisimme pysähtyä hetkeksi ja miettiä strategiaamme uudelleen.
> Mitä ongelmia yritämme oikeastaan ratkaista?
[Tilanhallinta](https://en.wikipedia.org/wiki/State_management) tarkoittaa hyvän lähestymistavan löytämistä näiden kahden erityisen ongelman ratkaisemiseksi:
- Miten pitää sovelluksen datavirrat ymmärrettävinä?
- Miten varmistaa, että tila ja käyttöliittymä ovat aina synkronissa (ja päinvastoin)?
- Kuinka pitää sovelluksen datavirrat ymmärrettävinä?
- Kuinka pitää tiladata aina synkronoituna käyttöliittymän kanssa (ja päinvastoin)?
Kun nämä asiat on hoidettu, muut mahdolliset ongelmat voivat joko ratketa itsestään tai niiden ratkaiseminen helpottuu. Näiden ongelmien ratkaisemiseksi on monia mahdollisia lähestymistapoja, mutta valitsemme yleisen ratkaisun, joka koostuu **datan ja sen muuttamistapojen keskittämisestä**. Datavirrat kulkevat seuraavasti:
Kun nämä asiat on hoidettu, muut mahdolliset ongelmat saattavat joko ratketa itsestään tai tulla helpommin ratkaistaviksi. Näiden ongelmien ratkaisemiseksi on monia mahdollisia lähestymistapoja, mutta valitsemme yleisen ratkaisun, joka koostuu **datan ja sen muuttamistapojen keskittämisestä**. Datavirrat kulkisivat seuraavasti:

> Emme käsittele tässä osassa sitä, miten data automaattisesti laukaisee näkymän päivityksen, sillä se liittyy edistyneempiin [reaktiivisen ohjelmoinnin](https://en.wikipedia.org/wiki/Reactive_programming) käsitteisiin. Tämä on hyvä jatkoaihe, jos haluat syventyä aiheeseen.
> Emme käsittele tässä osassa sitä, miten data automaattisesti päivittää näkymän, sillä se liittyy edistyneempiin [reaktiivisen ohjelmoinnin](https://en.wikipedia.org/wiki/Reactive_programming) käsitteisiin. Tämä on hyvä jatkoaihe, jos haluat syventyä aiheeseen.
✅ Markkinoilla on paljon kirjastoja, joilla on erilaisia lähestymistapoja tilanhallintaan, joista [Redux](https://redux.js.org) on suosittu vaihtoehto. Tutustu sen käsitteisiin ja malleihin, sillä ne tarjoavat usein hyvän tavan oppia, millaisia ongelmia saatat kohdata suurissa verkkosovelluksissa ja miten ne voidaan ratkaista.
✅ Markkinoilla on paljon kirjastoja, joilla on erilaisia lähestymistapoja tilanhallintaan, esimerkiksi [Redux](https://redux.js.org), joka on suosittu vaihtoehto. Tutustu sen käsitteisiin ja malleihin, sillä ne tarjoavat usein hyvän tavan oppia, millaisia ongelmia saatat kohdata suurissa verkkosovelluksissa ja miten ne voidaan ratkaista.
### Tehtävä
@ -75,9 +75,9 @@ let state = {
};
```
Ajatuksena on *keskittää* kaikki sovelluksen data yhteen tilan objektiin. Tällä hetkellä tilassa on vain `account`, joten muutos ei ole suuri, mutta se luo pohjan tuleville kehityksille.
Ajatuksena on *keskittää* kaikki sovelluksemme data yhteen tilaobjektiin. Meillä on toistaiseksi vain `account` tilassa, joten tämä ei muuta paljoa, mutta luo pohjan tuleville kehityksille.
Meidän täytyy myös päivittää sitä käyttävät funktiot. `register()`- ja `login()`-funktioissa korvaa `account = ...` seuraavalla: `state.account = ...`;
Meidän on myös päivitettävä sitä käyttävät funktiot. `register()`- ja `login()`-funktioissa korvaa `account = ...` seuraavalla: `state.account = ...`;
Lisää `updateDashboard()`-funktion alkuun tämä rivi:
@ -85,17 +85,17 @@ Lisää `updateDashboard()`-funktion alkuun tämä rivi:
const account = state.account;
```
Tämä refaktorointi ei itsessään tuonut suuria parannuksia, mutta sen tarkoituksena oli luoda perusta seuraaville muutoksille.
Tämä refaktorointi itsessään ei tuonut suuria parannuksia, mutta sen tarkoituksena oli luoda perusta seuraaville muutoksille.
## Datan muutosten seuraaminen
Nyt kun olemme ottaneet käyttöön `state`-objektin datan tallentamiseen, seuraava askel on keskittää päivitykset. Tavoitteena on helpottaa muutosten ja niiden ajankohdan seuraamista.
Nyt kun olemme ottaneet käyttöön `state`-objektin datan tallentamista varten, seuraava askel on keskittää päivitykset. Tavoitteena on helpottaa muutosten ja niiden ajankohdan seuraamista.
Jotta `state`-objektiin ei tehtäisi suoria muutoksia, on myös hyvä käytäntö pitää se [*muuttumattomana*](https://en.wikipedia.org/wiki/Immutable_object), mikä tarkoittaa, että sitä ei voi muokata lainkaan. Tämä tarkoittaa myös, että jos haluat muuttaa jotain siinä, sinun täytyy luoda uusi tilaobjekti. Näin suojaudut mahdollisilta ei-toivotuilta [sivuvaikutuksilta](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) ja avaat mahdollisuuksia uusille ominaisuuksille, kuten kumoa/tee uudelleen -toiminnon toteuttamiselle, samalla kun virheiden jäljittäminen helpottuu. Esimerkiksi voit kirjata kaikki tilaan tehdyt muutokset ja pitää kirjaa muutosten historiasta ymmärtääksesi virheen lähteen.
Jotta `state`-objektia ei voitaisi muuttaa suoraan, on myös hyvä käytäntö pitää se [*muuttumattomana*](https://en.wikipedia.org/wiki/Immutable_object), mikä tarkoittaa, että sitä ei voi muokata lainkaan. Tämä tarkoittaa myös, että sinun on luotava uusi tilaobjekti, jos haluat muuttaa jotain siinä. Näin rakennat suojan mahdollisia ei-toivottuja [sivuvaikutuksia](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) vastaan ja avaat mahdollisuuksia uusille ominaisuuksille, kuten kumoa/tee uudelleen -toiminnon toteuttamiselle, samalla kun helpotat virheiden jäljittämistä. Esimerkiksi voit kirjata kaikki tilaan tehdyt muutokset ja pitää niistä historian ymmärtääksesi virheen lähteen.
JavaScriptissä voit käyttää [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)-metodia luodaksesi muuttumattoman version objektista. Jos yrität tehdä muutoksia muuttumattomaan objektiin, syntyy poikkeus.
✅ Tiedätkö eron *pintapuolisen* ja *syvän* muuttumattoman objektin välillä? Voit lukea siitä [täältä](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Tiedätkö eron *pintapuolisen* ja *syvän* muuttumattoman objektin välillä? Voit lukea siitä lisää [täältä](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Tehtävä
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
Tässä funktiossa luomme uuden tilaobjektin ja kopioimme tiedot edellisestä tilasta käyttämällä [*spread-operaattoria (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Sitten ylikirjoitamme tietyn tilaobjektin ominaisuuden uusilla tiedoilla käyttämällä [hakasuljenotaatioita](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` määrittelyyn. Lopuksi lukitsemme objektin estääksemme muutokset käyttämällä `Object.freeze()`-metodia. Tällä hetkellä tilassa on vain `account`, mutta tällä lähestymistavalla voit lisätä niin monta ominaisuutta kuin tarvitset.
Tässä funktiossa luomme uuden tilaobjektin ja kopioimme tiedot edellisestä tilasta käyttämällä [*spread-operaattoria (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Sitten ylikirjoitamme tietyn tilaobjektin ominaisuuden uusilla tiedoilla käyttämällä [sulkunotaatiota](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` määrittelyyn. Lopuksi lukitsemme objektin estääksemme muutokset käyttämällä `Object.freeze()`-metodia. Tällä hetkellä meillä on vain `account`-ominaisuus tallennettuna tilaan, mutta tällä lähestymistavalla voit lisätä niin monta ominaisuutta kuin tarvitset.
Päivitetään myös `state`-alustus varmistamaan, että alkuperäinen tila on myös jäädytetty:
Päivitämme myös `state`-alkuinitoinnin varmistaaksemme, että alkuperäinen tila on myös jäädytetty:
```js
let state = Object.freeze({
@ -126,13 +126,13 @@ Tämän jälkeen päivitä `register`-funktio korvaamalla `state.account = resul
updateState('account', result);
```
Tee sama `login`-funktiolle korvaamalla `state.account = data;` seuraavalla:
Tee sama `login`-funktion kanssa, korvaamalla `state.account = data;` seuraavalla:
```js
updateState('account', data);
```
Korjataan samalla ongelma, jossa tilitietoja ei tyhjennetä, kun käyttäjä napsauttaa *Kirjaudu ulos*.
Korjataan samalla tilitietojen säilymättömyys, kun käyttäjä napsauttaa *Kirjaudu ulos*.
Kokeile rekisteröidä uusi tili, kirjautua ulos ja takaisin sisään varmistaaksesi, että kaikki toimii edelleen oikein.
Kokeile rekisteröidä uusi tili, kirjautua ulos ja takaisin sisään varmistaaksesi, että kaikki toimii oikein.
> Vinkki: voit tarkastella kaikkia tilan muutoksia lisäämällä `console.log(state)``updateState()`-funktion loppuun ja avaamalla selaimen kehitystyökalujen konsolin.
> Vinkki: Voit tarkastella kaikkia tilan muutoksia lisäämällä `console.log(state)``updateState()`-funktion loppuun ja avaamalla selaimen kehitystyökalujen konsolin.
## Tilan säilyttäminen
Useimmat verkkosovellukset tarvitsevat datan säilyttämistä toimiakseen oikein. Kaikki kriittiset tiedot tallennetaan yleensä tietokantaan ja haetaan palvelin-API:n kautta, kuten käyttäjätilitiedot meidän tapauksessamme. Mutta joskus on myös hyödyllistä säilyttää joitakin tietoja selaimessa toimivassa asiakassovelluksessa paremman käyttökokemuksen tai latausnopeuden parantamiseksi.
Useimmat verkkosovellukset tarvitsevat datan säilyttämistä toimiakseen oikein. Kaikki kriittiset tiedot tallennetaan yleensä tietokantaan ja niitä käytetään palvelin-API:n kautta, kuten käyttäjätilitietoja meidän tapauksessamme. Joskus on kuitenkin hyödyllistä säilyttää joitakin tietoja selaimessa toimivassa asiakassovelluksessa paremman käyttökokemuksen tai latausnopeuden parantamiseksi.
Kun haluat säilyttää tietoja selaimessasi, on hyvä kysyä itseltäsi muutamia tärkeitä kysymyksiä:
Kun haluat säilyttää tietoja selaimessasi, sinun tulisi kysyä itseltäsi muutamia tärkeitä kysymyksiä:
- *Ovatko tiedot arkaluonteisia?*Vältä arkaluonteisten tietojen, kuten käyttäjän salasanojen, tallentamista asiakaspuolelle.
- *Kuinka kauan tarvitset näitä tietoja?* Aiotko käyttää tietoja vain nykyisen istunnon aikana vai haluatko tallentaa ne pysyvästi?
- *Ovatko tiedot arkaluonteisia?*Sinun tulisi välttää arkaluonteisten tietojen, kuten käyttäjäsalasanojen, tallentamista asiakassovellukseen.
- *Kuinka kauan tarvitset näitä tietoja?* Aiotko käyttää näitä tietoja vain nykyisen istunnon ajan vai haluatko niiden säilyvän ikuisesti?
Tietojen tallentamiseen verkkosovelluksessa on useita tapoja sen mukaan, mitä haluat saavuttaa. Esimerkiksi voit käyttää URL-osoitteita tallentaaksesi hakukyselyn ja tehdäksesi siitä jaettavan käyttäjien kesken. Voit myös käyttää [HTTP-evästeitä](https://developer.mozilla.org/docs/Web/HTTP/Cookies), jos tiedot täytyy jakaa palvelimen kanssa, kuten [autentikointitiedot](https://en.wikipedia.org/wiki/Authentication).
Tietojen tallentamiseen verkkosovelluksessa on useita tapoja, riippuen siitä, mitä haluat saavuttaa. Esimerkiksi voit käyttää URL-osoitteita tallentaaksesi hakukyselyn ja tehdäksesi sen jaettavaksi käyttäjien kesken. Voit myös käyttää [HTTP-evästeitä](https://developer.mozilla.org/docs/Web/HTTP/Cookies), jos tiedot täytyy jakaa palvelimen kanssa, kuten [todennustiedot](https://en.wikipedia.org/wiki/Authentication).
Toinen vaihtoehto on käyttää jotakin selaimen monista API-rajapinnoista tietojen tallentamiseen. Kaksi erityisen kiinnostavaa vaihtoehtoa ovat:
Toinen vaihtoehto on käyttää jotakin selaimen monista API:sta tietojen tallentamiseen. Kaksi niistä on erityisen kiinnostavia:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Avain/Arvo-tietokanta](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), joka mahdollistaa tietojen säilyttämisen tietylle verkkosivustolle eri istuntojen välillä. Tallennetut tiedot eivät koskaan vanhene.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): Tämä toimii samalla tavalla kuin `localStorage`, mutta tallennetut tiedot poistetaan, kun istunto päättyy (kun selain suljetaan).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Avain/Arvo-tietokanta](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), joka mahdollistaa tietojen säilyttämisen tietyn verkkosivuston osalta eri istuntojen välillä. Tallennetut tiedot eivät koskaan vanhene.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): Tämä toimii samalla tavalla kuin `localStorage`, paitsi että tallennetut tiedot poistetaan, kun istunto päättyy (kun selain suljetaan).
Huomaa, että molemmat näistä API-rajapinnoista sallivat vain [merkkijonojen](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) tallentamisen. Jos haluat tallentaa monimutkaisia objekteja, sinun täytyy sarjoittaa ne [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-muotoon käyttämällä [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)-metodia.
Huomaa, että molemmat näistä API:sta sallivat vain [merkkijonojen](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) tallentamisen. Jos haluat tallentaa monimutkaisia objekteja, sinun täytyy sarjoittaa ne [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-muotoon käyttämällä [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)-metodia.
✅ Jos haluat luoda verkkosovelluksen, joka ei toimi palvelimen kanssa, on myös mahdollista luoda tietokanta asiakaspuolelle käyttämällä [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API)-rajapintaa. Tämä on varattu edistyneisiin käyttötapauksiin tai jos sinun täytyy tallentaa merkittävä määrä tietoa, sillä sen käyttö on monimutkaisempaa.
✅ Jos haluat luoda verkkosovelluksen, joka ei toimi palvelimen kanssa, on myös mahdollista luoda tietokanta asiakassovellukseen käyttämällä [`IndexedDB` API:ta](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Tämä on varattu edistyneempiin käyttötapauksiin tai jos sinun täytyy tallentaa merkittävä määrä tietoa, sillä sen käyttö on monimutkaisempaa.
### Tehtävä
@ -183,9 +183,9 @@ Lisää sitten tämä rivi `updateState()`-funktion loppuun:
Tämän avulla käyttäjän tilitiedot säilyvät ja pysyvät ajan tasalla, koska olemme aiemmin keskittäneet kaikki tilapäivitykset. Tässä kohtaa alamme hyötyä kaikista aiemmista refaktoroinneistamme 🙂.
Tämän avulla käyttäjätilitiedot säilyvät ja pysyvät aina ajan tasalla, koska olemme aiemmin keskittäneet kaikki tilapäivityksemme. Tässä vaiheessa alamme hyötyä kaikista aiemmista refaktoroinneistamme 🙂.
Koska tiedot tallennetaan, meidän täytyy myös huolehtia niiden palauttamisesta, kun sovellus ladataan. Koska alamme saada enemmän alustuskoodia, voi olla hyvä idea luoda uusi `init`-funktio, joka sisältää myös aiemman koodimme `app.js`-tiedoston lopussa:
Koska tiedot tallennetaan, meidän on myös huolehdittava niiden palauttamisesta, kun sovellus ladataan. Koska alamme saada enemmän alustuskoodia, voi olla hyvä idea luoda uusi `init`-funktio, joka sisältää myös aiemman koodimme `app.js`-tiedoston lopussa:
```js
function init() {
@ -202,7 +202,7 @@ function init() {
init();
```
Tässä haemme tallennetut tiedot, ja jos niitä löytyy, päivitämme tilan vastaavasti. On tärkeää tehdä tämä *ennen* reitin päivitystä, sillä sivun päivityksen aikana voi olla koodia, joka riippuu tilasta.
Tässä haemme tallennetut tiedot, ja jos niitä on, päivitämme tilan vastaavasti. On tärkeää tehdä tämä *ennen* reitin päivitystä, sillä sivun päivityksen aikana voi olla koodia, joka riippuu tilasta.
Voimme myös tehdä *Hallintapaneeli*-sivusta sovelluksemme oletussivun, koska nyt säilytämme tilitiedot. Jos tietoja ei löydy, hallintapaneeli huolehtii uudelleenohjauksesta *Kirjautumissivulle*. Korvaa `updateRoute()`-funktiossa oletus `return navigate('/login');` seuraavalla: `return navigate('/dashboard');`.
Kokeile päivittää hallintapaneelin sivu selaimessa nyt. Mitä tapahtuu? Näetkö uuden tapahtuman?
Kokeile päivittää hallintapaneelisivusi selaimessa nyt. Mitä tapahtuu? Näetkö uuden tapahtuman?
Tila säilyy loputtomasti`localStorage`-ominaisuuden ansiosta, mutta se tarkoittaa myös, että sitä ei koskaan päivitetä, ennen kuin kirjaudut ulos sovelluksesta ja takaisin sisään!
Tila säilyy loputtomiin`localStorage`-ominaisuuden ansiosta, mutta se tarkoittaa myös, että sitä ei koskaan päivitetä, ennen kuin kirjaudut ulos sovelluksesta ja takaisin sisään!
Yksi mahdollinen strategia tämän korjaamiseksi on ladata tilitiedot uudelleen aina, kun hallintapaneeli ladataan, jotta vältetään vanhentuneet tiedot.
@ -258,7 +258,7 @@ async function refresh() {
}
```
Tämä funktio päivittää tilitiedot ja huolehtii sitten hallintapaneelin HTML:n päivittämisestä. Tätä meidän täytyy kutsua, kun hallintapaneelin reitti ladataan. Päivitä reitin määrittely seuraavasti:
Tämä funktio päivittää tilitiedot ja huolehtii hallintapaneelisivun HTML:n päivittämisestä. Tätä meidän täytyy kutsua, kun hallintapaneelireitti ladataan. Päivitä reittimääritelmä seuraavasti:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
Kokeile päivittää hallintapaneeli nyt, sen pitäisi näyttää päivitetyt tilitiedot.
Kokeile nyt päivittää hallintapaneeli, sen pitäisi näyttää päivitetyt tilitiedot.
---
## 🚀 Haaste
Nyt kun lataamme tilitiedot uudelleen aina, kun hallintapaneeli ladataan, luuletko, että meidän täytyy yhä säilyttää *kaikki tilitiedot*?
Nyt kun lataamme tilitiedot uudelleen aina, kun hallintapaneeli ladataan, luuletko, että meidän täytyy edelleen säilyttää *kaikki tilitiedot*?
Kokeile työskennellä yhdessä muuttaaksesi, mitä tallennetaan ja ladataan `localStorage`-ominaisuudesta, niin että mukana on vain se, mikä on ehdottoman välttämätöntä sovelluksen toiminnan kannalta.
Yrittäkää yhdessä muuttaa, mitä tallennetaan ja ladataan `localStorage`-ominaisuudesta, niin että mukana on vain se, mikä on ehdottoman välttämätöntä sovelluksen toiminnan kannalta.
Tässä on esimerkkitulos tehtävän suorittamisen jälkeen:


---
**Vastuuvapauslauseke**:
Tämä asiakirja on käännetty käyttämällä tekoälypohjaista käännöspalvelua [Co-op Translator](https://github.com/Azure/co-op-translator). Pyrimme tarkkuuteen, mutta huomioithan, että automaattiset käännökset voivat sisältää virheitä tai epätarkkuuksia. Alkuperäistä asiakirjaa sen alkuperäisellä kielellä tulee pitää ensisijaisena lähteenä. Kriittisen tiedon osalta suositellaan ammattimaista ihmiskääntämistä. Emme ole vastuussa väärinkäsityksistä tai virhetulkinnoista, jotka johtuvat tämän käännöksen käytöstä.
Tämä asiakirja on käännetty käyttämällä tekoälypohjaista käännöspalvelua [Co-op Translator](https://github.com/Azure/co-op-translator). Vaikka pyrimme tarkkuuteen, huomioithan, että automaattiset käännökset voivat sisältää virheitä tai epätarkkuuksia. Alkuperäistä asiakirjaa sen alkuperäisellä kielellä tulisi pitää ensisijaisena lähteenä. Kriittisen tiedon osalta suositellaan ammattimaista ihmiskäännöstä. Emme ole vastuussa tämän käännöksen käytöstä johtuvista väärinkäsityksistä tai virhetulkinnoista.
À mesure qu'une application web grandit, il devient difficile de suivre tous les flux de données. Quel code obtient les données, quelle page les consomme, où et quand doivent-elles être mises à jour... il est facile de finir avec un code désordonné et difficile à maintenir. Cela est particulièrement vrai lorsque vous devez partager des données entre différentes pages de votre application, comme les données utilisateur. Le concept de *gestion d'état* a toujours existé dans tous les types de programmes, mais à mesure que les applications web deviennent de plus en plus complexes, c'est désormais un point clé à considérer lors du développement.
À mesure qu'une application web se développe, il devient difficile de suivre tous les flux de données. Quel code obtient les données, quelle page les utilise, où et quand elles doivent être mises à jour... il est facile de se retrouver avec un code désordonné et difficile à maintenir. Cela est particulièrement vrai lorsque vous devez partager des données entre différentes pages de votre application, comme les données utilisateur. Le concept de *gestion d'état* a toujours existé dans tous les types de programmes, mais avec la complexité croissante des applications web, il est désormais essentiel d'y réfléchir dès le développement.
Dans cette dernière partie, nous allons examiner l'application que nous avons construite pour repenser la gestion de l'état, permettant ainsi de prendre en charge le rafraîchissement du navigateur à tout moment et de conserver les données entre les sessions utilisateur.
Dans cette dernière partie, nous allons examiner l'application que nous avons construite pour repenser la gestion de l'état, permettant de prendre en charge le rafraîchissement du navigateur à tout moment et de conserver les données entre les sessions utilisateur.
### Prérequis
Vous devez avoir terminé la partie [récupération de données](../3-data/README.md) de l'application web pour cette leçon. Vous devez également installer [Node.js](https://nodejs.org) et [exécuter l'API serveur](../api/README.md) localement pour pouvoir gérer les données de compte.
Vous devez avoir terminé la partie [récupération de données](../3-data/README.md) de l'application web pour cette leçon. Vous devez également installer [Node.js](https://nodejs.org) et [exécuter l'API serveur](../api/README.md) localement afin de pouvoir gérer les données de compte.
Vous pouvez tester si le serveur fonctionne correctement en exécutant cette commande dans un terminal :
Vous pouvez tester que le serveur fonctionne correctement en exécutant cette commande dans un terminal :
```sh
curl http://localhost:5000/api
@ -34,34 +34,34 @@ curl http://localhost:5000/api
## Repenser la gestion d'état
Dans la [leçon précédente](../3-data/README.md), nous avons introduit un concept de base de l'état dans notre application avec la variable globale `account` qui contient les données bancaires de l'utilisateur actuellement connecté. Cependant, notre implémentation actuelle présente quelques défauts. Essayez de rafraîchir la page lorsque vous êtes sur le tableau de bord. Que se passe-t-il ?
Dans la [leçon précédente](../3-data/README.md), nous avons introduit un concept basique d'état dans notre application avec la variable globale `account` qui contient les données bancaires de l'utilisateur actuellement connecté. Cependant, notre implémentation actuelle présente quelques défauts. Essayez de rafraîchir la page lorsque vous êtes sur le tableau de bord. Que se passe-t-il ?
Il y a 3 problèmes avec le code actuel :
Il y a trois problèmes avec le code actuel :
- L'état n'est pas persistant, car un rafraîchissement du navigateur vous ramène à la page de connexion.
- Il existe plusieurs fonctions qui modifient l'état. À mesure que l'application grandit, cela peut rendre difficile le suivi des modifications et il est facile d'oublier d'en mettre une à jour.
- L'état n'est pas conservé, un rafraîchissement du navigateur vous ramène à la page de connexion.
- Plusieurs fonctions modifient l'état. À mesure que l'application se développe, cela peut rendre difficile le suivi des changements et il est facile d'oublier de mettre à jour une partie.
- L'état n'est pas nettoyé, donc lorsque vous cliquez sur *Déconnexion*, les données du compte sont toujours présentes même si vous êtes sur la page de connexion.
Nous pourrions mettre à jour notre code pour résoudre ces problèmes un par un, mais cela créerait plus de duplication de code et rendrait l'application plus complexe et difficile à maintenir. Ou nous pourrions prendre quelques minutes pour repenser notre stratégie.
Nous pourrions mettre à jour notre code pour résoudre ces problèmes un par un, mais cela entraînerait une duplication de code et rendrait l'application plus complexe et difficile à maintenir. Ou nous pourrions prendre quelques minutes pour repenser notre stratégie.
> Quels problèmes essayons-nous vraiment de résoudre ici ?
La [gestion d'état](https://fr.wikipedia.org/wiki/Gestion_d%27%C3%A9tat) consiste à trouver une bonne approche pour résoudre ces deux problèmes particuliers :
La [gestion d'état](https://en.wikipedia.org/wiki/State_management) consiste à trouver une bonne approche pour résoudre ces deux problèmes particuliers :
- Comment garder les flux de données dans une application compréhensibles ?
- Comment garder les flux de données d'une application compréhensibles ?
- Comment maintenir les données d'état toujours synchronisées avec l'interface utilisateur (et vice versa) ?
Une fois que vous avez pris soin de ces points, tout autre problème que vous pourriez rencontrer pourrait déjà être résolu ou devenir plus facile à résoudre. Il existe de nombreuses approches possibles pour résoudre ces problèmes, mais nous opterons pour une solution courante qui consiste à **centraliser les données et les moyens de les modifier**. Les flux de données fonctionneraient comme suit :
Une fois ces problèmes résolus, tout autre problème que vous pourriez rencontrer pourrait déjà être corrigé ou devenir plus facile à résoudre. Il existe de nombreuses approches possibles pour résoudre ces problèmes, mais nous opterons pour une solution courante qui consiste à **centraliser les données et les moyens de les modifier**. Les flux de données fonctionneraient ainsi :

> Nous ne couvrirons pas ici la partie où les données déclenchent automatiquement la mise à jour de la vue, car cela est lié à des concepts plus avancés de [programmation réactive](https://fr.wikipedia.org/wiki/Programmation_r%C3%A9active). C'est un bon sujet de suivi si vous êtes prêt pour une plongée approfondie.
> Nous ne couvrirons pas ici la partie où les données déclenchent automatiquement la mise à jour de la vue, car elle est liée à des concepts plus avancés de [programmation réactive](https://en.wikipedia.org/wiki/Reactive_programming). C'est un bon sujet à approfondir si vous êtes prêt à plonger plus profondément.
✅ Il existe de nombreuses bibliothèques avec différentes approches pour la gestion d'état, [Redux](https://redux.js.org) étant une option populaire. Jetez un œil aux concepts et aux modèles utilisés, car cela permet souvent de comprendre les problèmes potentiels auxquels vous pourriez être confronté dans de grandes applications web et comment les résoudre.
✅ Il existe de nombreuses bibliothèques avec différentes approches de gestion d'état, [Redux](https://redux.js.org) étant une option populaire. Consultez les concepts et les modèles utilisés, car cela peut souvent être un bon moyen d'apprendre quels problèmes potentiels vous pourriez rencontrer dans les grandes applications web et comment ils peuvent être résolus.
### Tâche
Nous allons commencer par un peu de refactorisation. Remplacez la déclaration `account` :
Nous allons commencer par un peu de refactoring. Remplacez la déclaration `account` :
```js
let account = null;
@ -75,27 +75,27 @@ let state = {
};
```
L'idée est de *centraliser* toutes les données de notre application dans un seul objet d'état. Nous n'avons que `account` pour l'instant dans l'état, donc cela ne change pas grand-chose, mais cela ouvre la voie à des évolutions.
L'idée est de *centraliser* toutes les données de notre application dans un seul objet d'état. Nous n'avons pour l'instant que `account` dans l'état, donc cela ne change pas grand-chose, mais cela ouvre la voie à des évolutions.
Nous devons également mettre à jour les fonctions qui l'utilisent. Dans les fonctions `register()` et `login()`, remplacez `account = ...` par `state.account = ...`;
En haut de la fonction `updateDashboard()`, ajoutez cette ligne :
Au début de la fonction `updateDashboard()`, ajoutez cette ligne :
```js
const account = state.account;
```
Ce refactoring en lui-même n'a pas apporté beaucoup d'améliorations, mais l'idée était de poser les bases pour les prochains changements.
Ce refactoring en lui-même n'a pas apporté beaucoup d'améliorations, mais l'idée était de poser les bases des prochains changements.
## Suivre les modifications des données
## Suivre les changements de données
Maintenant que nous avons mis en place l'objet `state` pour stocker nos données, l'étape suivante consiste à centraliser les mises à jour. L'objectif est de faciliter le suivi de toutes les modifications et de savoir quand elles se produisent.
Maintenant que nous avons mis en place l'objet `state` pour stocker nos données, l'étape suivante consiste à centraliser les mises à jour. L'objectif est de faciliter le suivi de tout changement et de savoir quand ils se produisent.
Pour éviter que des modifications soient apportées à l'objet `state`, il est également judicieux de le considérer comme [*immuable*](https://fr.wikipedia.org/wiki/Objet_immuable), ce qui signifie qu'il ne peut pas être modifié du tout. Cela signifie également que vous devez créer un nouvel objet d'état si vous souhaitez modifier quoi que ce soit. En procédant ainsi, vous vous protégez contre des [effets de bord](https://fr.wikipedia.org/wiki/Effet_de_bord_(informatique)) potentiellement indésirables et ouvrez des possibilités pour de nouvelles fonctionnalités dans votre application, comme la mise en œuvre d'annulations/rétablissements, tout en facilitant le débogage. Par exemple, vous pourriez enregistrer chaque modification apportée à l'état et conserver un historique des modifications pour comprendre l'origine d'un bug.
Pour éviter que des modifications soient apportées à l'objet `state`, il est également recommandé de le considérer comme [*immuable*](https://en.wikipedia.org/wiki/Immutable_object), ce qui signifie qu'il ne peut pas être modifié du tout. Cela implique également que vous devez créer un nouvel objet d'état si vous souhaitez modifier quoi que ce soit. En procédant ainsi, vous vous protégez contre des [effets secondaires](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) indésirables et ouvrez des possibilités pour de nouvelles fonctionnalités dans votre application, comme la mise en œuvre d'annulation/rétablissement, tout en facilitant le débogage. Par exemple, vous pourriez enregistrer chaque changement apporté à l'état et conserver un historique des modifications pour comprendre l'origine d'un bug.
En JavaScript, vous pouvez utiliser [`Object.freeze()`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) pour créer une version immuable d'un objet. Si vous essayez d'apporter des modifications à un objet immuable, une exception sera levée.
En JavaScript, vous pouvez utiliser [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) pour créer une version immuable d'un objet. Si vous essayez de modifier un objet immuable, une exception sera levée.
✅ Connaissez-vous la différence entre un objet immuable *superficiel* et *profond* ? Vous pouvez en lire davantage [ici](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#Qu%27est-ce_qu%27un_freeze_superficiel).
✅ Connaissez-vous la différence entre un objet immuable *superficiel* et *profond* ? Vous pouvez en lire davantage [ici](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Tâche
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
Dans cette fonction, nous créons un nouvel objet d'état et copions les données de l'état précédent en utilisant l'[opérateur de décomposition (`...`)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/Spread_syntax#D%C3%A9composition_dans_les_litt%C3%A9raux_d%27objets). Ensuite, nous remplaçons une propriété particulière de l'objet d'état avec les nouvelles données en utilisant la [notation entre crochets](https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Travail_avec_les_objets#Objets_et_propri%C3%A9t%C3%A9s) `[propriété]` pour l'affectation. Enfin, nous verrouillons l'objet pour empêcher les modifications en utilisant `Object.freeze()`. Nous n'avons pour l'instant que la propriété `account` stockée dans l'état, mais avec cette approche, vous pouvez ajouter autant de propriétés que nécessaire dans l'état.
Dans cette fonction, nous créons un nouvel objet d'état et copions les données de l'état précédent en utilisant le [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Ensuite, nous remplaçons une propriété particulière de l'objet d'état avec les nouvelles données en utilisant la [notation entre crochets](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` pour l'affectation. Enfin, nous verrouillons l'objet pour empêcher les modifications en utilisant `Object.freeze()`. Pour l'instant, nous n'avons que la propriété `account` stockée dans l'état, mais avec cette approche, vous pouvez ajouter autant de propriétés que nécessaire.
Nous mettrons également à jour l'initialisation de `state` pour nous assurer que l'état initial est également figé :
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
Ensuite, mettez à jour la fonction `register` en remplaçant l'affectation `state.account = result;` par :
Après cela, mettez à jour la fonction `register` en remplaçant l'affectation `state.account = result;` par :
```js
updateState('account', result);
@ -143,31 +143,31 @@ function logout() {
}
```
Dans `updateDashboard()`, remplacez la redirection `return navigate('/login');` par `return logout();`
Dans `updateDashboard()`, remplacez la redirection `return navigate('/login');` par `return logout()`;
Essayez de créer un nouveau compte, de vous déconnecter et de vous reconnecter pour vérifier que tout fonctionne toujours correctement.
> Astuce : vous pouvez examiner toutes les modifications d'état en ajoutant `console.log(state)` à la fin de `updateState()` et en ouvrant la console dans les outils de développement de votre navigateur.
> Astuce : vous pouvez examiner tous les changements d'état en ajoutant `console.log(state)` à la fin de `updateState()` et en ouvrant la console dans les outils de développement de votre navigateur.
## Persister l'état
## Conserver l'état
La plupart des applications web ont besoin de persister des données pour fonctionner correctement. Toutes les données critiques sont généralement stockées dans une base de données et accessibles via une API serveur, comme les données de compte utilisateur dans notre cas. Mais parfois, il est également intéressant de persister certaines données sur l'application cliente qui s'exécute dans votre navigateur, pour une meilleure expérience utilisateur ou pour améliorer les performances de chargement.
La plupart des applications web ont besoin de conserver des données pour fonctionner correctement. Toutes les données critiques sont généralement stockées dans une base de données et accessibles via une API serveur, comme les données de compte utilisateur dans notre cas. Mais parfois, il est également intéressant de conserver certaines données dans l'application cliente qui s'exécute dans votre navigateur, pour une meilleure expérience utilisateur ou pour améliorer les performances de chargement.
Lorsque vous souhaitez persister des données dans votre navigateur, il y a quelques questions importantes à vous poser :
Lorsque vous souhaitez conserver des données dans votre navigateur, il y a quelques questions importantes à vous poser :
- *Les données sont-elles sensibles ?* Vous devriez éviter de stocker des données sensibles côté client, comme les mots de passe utilisateur.
- *Pendant combien de temps avez-vous besoin de conserver ces données ?* Prévoyez-vous d'accéder à ces données uniquement pour la session en cours ou souhaitez-vous qu'elles soient stockées indéfiniment ?
Il existe plusieurs façons de stocker des informations dans une application web, en fonction de ce que vous souhaitez accomplir. Par exemple, vous pouvez utiliser les URLs pour stocker une requête de recherche et la rendre partageable entre utilisateurs. Vous pouvez également utiliser les [cookies HTTP](https://developer.mozilla.org/fr/docs/Web/HTTP/Cookies) si les données doivent être partagées avec le serveur, comme les informations d'[authentification](https://fr.wikipedia.org/wiki/Authentification).
Il existe plusieurs façons de stocker des informations dans une application web, en fonction de ce que vous souhaitez accomplir. Par exemple, vous pouvez utiliser les URL pour stocker une requête de recherche et la rendre partageable entre utilisateurs. Vous pouvez également utiliser les [cookies HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) si les données doivent être partagées avec le serveur, comme les informations d'[authentification](https://en.wikipedia.org/wiki/Authentication).
Une autre option consiste à utiliser l'une des nombreuses API de navigateur pour stocker des données. Deux d'entre elles sont particulièrement intéressantes :
- [`localStorage`](https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage) : un [stockage clé/valeur](https://fr.wikipedia.org/wiki/Base_de_donn%C3%A9es_cl%C3%A9-valeur) permettant de persister des données spécifiques au site web actuel entre différentes sessions. Les données enregistrées dans celui-ci n'expirent jamais.
- [`sessionStorage`](https://developer.mozilla.org/fr/docs/Web/API/Window/sessionStorage) : celui-ci fonctionne de la même manière que `localStorage`, sauf que les données stockées sont effacées lorsque la session se termine (lorsque le navigateur est fermé).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage) : un [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) permettant de conserver des données spécifiques au site web actuel entre différentes sessions. Les données enregistrées dans celui-ci n'expirent jamais.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage) : celui-ci fonctionne de la même manière que `localStorage`, sauf que les données stockées sont effacées lorsque la session se termine (lorsque le navigateur est fermé).
Notez que ces deux API ne permettent de stocker que des [chaînes de caractères](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/String). Si vous souhaitez stocker des objets complexes, vous devrez les sérialiser au format [JSON](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/JSON) en utilisant [`JSON.stringify()`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Notez que ces deux API permettent uniquement de stocker des [chaînes de caractères](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Si vous souhaitez stocker des objets complexes, vous devrez les sérialiser au format [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) en utilisant [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Si vous souhaitez créer une application web qui ne fonctionne pas avec un serveur, il est également possible de créer une base de données côté client en utilisant l'[API `IndexedDB`](https://developer.mozilla.org/fr/docs/Web/API/IndexedDB_API). Celle-ci est réservée aux cas d'utilisation avancés ou si vous avez besoin de stocker une quantité importante de données, car elle est plus complexe à utiliser.
✅ Si vous souhaitez créer une application web qui ne fonctionne pas avec un serveur, il est également possible de créer une base de données côté client en utilisant l'API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Celle-ci est réservée à des cas d'utilisation avancés ou si vous devez stocker une quantité importante de données, car elle est plus complexe à utiliser.
### Tâche
@ -183,9 +183,9 @@ Ajoutez ensuite cette ligne à la fin de la fonction `updateState()` :
Avec cela, les données du compte utilisateur seront persistées et toujours à jour, car nous avons centralisé toutes nos mises à jour d'état précédemment. C'est ici que nous commençons à bénéficier de tous nos refactorings précédents 🙂.
Avec cela, les données du compte utilisateur seront conservées et toujours à jour, car nous avons centralisé toutes nos mises à jour d'état précédemment. C'est ici que nous commençons à bénéficier de tous nos refactorings précédents 🙂.
Comme les données sont enregistrées, nous devons également nous occuper de les restaurer lorsque l'application est chargée. Puisque nous allons commencer à avoir plus de code d'initialisation, il peut être judicieux de créer une nouvelle fonction `init`, qui inclut également notre code précédent en bas de `app.js` :
Comme les données sont enregistrées, nous devons également nous occuper de leur restauration lorsque l'application est chargée. Étant donné que nous commencerons à avoir plus de code d'initialisation, il peut être judicieux de créer une nouvelle fonction `init`, qui inclut également notre code précédent en bas de `app.js` :
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Ici, nous récupérons les données enregistrées, et s'il y en a, nous mettons à jour l'état en conséquence. Il est important de faire cela *avant* de mettre à jour la route, car il pourrait y avoir du code qui dépend de l'état pendant la mise à jour de la page.
Ici, nous récupérons les données enregistrées, et s'il y en a, nous mettons à jour l'état en conséquence. Il est important de faire cela *avant* de mettre à jour la route, car il pourrait y avoir du code qui dépend de l'état lors de la mise à jour de la page.
Nous pouvons également faire de la page *Tableau de bord* la page par défaut de notre application, car nous persistons maintenant les données du compte. Si aucune donnée n'est trouvée, le tableau de bord s'occupe de rediriger vers la page *Connexion* de toute façon. Dans `updateRoute()`, remplacez le fallback `return navigate('/login');` par `return navigate('/dashboard');`.
Nous pouvons également faire de la page *Tableau de bord* la page par défaut de notre application, car nous conservons maintenant les données du compte. Si aucune donnée n'est trouvée, le tableau de bord s'occupe de rediriger vers la page *Connexion* de toute façon. Dans `updateRoute()`, remplacez le fallback `return navigate('/login');` par `return navigate('/dashboard');`.
Connectez-vous maintenant à l'application et essayez de rafraîchir la page. Vous devriez rester sur le tableau de bord. Avec cette mise à jour, nous avons résolu tous nos problèmes initiaux...
## Rafraîchir les données
...Mais nous pourrions aussi en avoir créé un nouveau. Oups !
...Mais nous pourrions également en avoir créé un nouveau. Oups !
Allez sur le tableau de bord en utilisant le compte `test`, puis exécutez cette commande dans un terminal pour créer une nouvelle transaction :
Accédez au tableau de bord en utilisant le compte `test`, puis exécutez cette commande dans un terminal pour créer une nouvelle transaction :
Essayez maintenant de rafraîchir la page du tableau de bord dans le navigateur. Que se passe-t-il ? Voyez-vous la nouvelle transaction ?
Essayez maintenant de rafraîchir votre page de tableau de bord dans le navigateur. Que se passe-t-il ? Voyez-vous la nouvelle transaction ?
L'état est persistant indéfiniment grâce à `localStorage`, mais cela signifie également qu'il n'est jamais mis à jour tant que vous ne vous déconnectez pas de l'application et ne vous reconnectez pas !
L'état est conservé indéfiniment grâce à `localStorage`, mais cela signifie également qu'il n'est jamais mis à jour jusqu'à ce que vous vous déconnectiez de l'application et vous reconnectiez !
Une stratégie possible pour résoudre cela est de recharger les données du compte chaque fois que le tableau de bord est chargé, pour éviter des données obsolètes.
Une stratégie possible pour résoudre cela est de recharger les données du compte chaque fois que le tableau de bord est chargé, afin d'éviter des données obsolètes.
### Tâche
@ -273,22 +273,22 @@ Essayez maintenant de recharger le tableau de bord, il devrait afficher les donn
## 🚀 Défi
Maintenant que nous rechargeons les données du compte chaque fois que le tableau de bord est chargé, pensez-vous que nous avons encore besoin de persister *toutes les données du compte* ?
Maintenant que nous rechargeons les données du compte chaque fois que le tableau de bord est chargé, pensez-vous que nous avons encore besoin de conserver *toutes les données du compte* ?
Essayez de travailler ensemble pour modifier ce qui est enregistré et chargé depuis `localStorage` afin d'inclure uniquement ce qui est absolument nécessaire pour que l'application fonctionne.
Essayez de travailler ensemble pour modifier ce qui est enregistré et chargé depuis `localStorage` afin d'inclure uniquement ce qui est absolument nécessaire au fonctionnement de l'application.
## Quiz après le cours
[Quiz après le cours](https://ff-quizzes.netlify.app/web/quiz/48)
## Devoir
[Mettre en œuvre la boîte de dialogue "Ajouter une transaction"](assignment.md)
Voici un exemple de résultat après avoir terminé la tâche :
[Implémenter la boîte de dialogue "Ajouter une transaction"](assignment.md)
Voici un exemple de résultat après avoir terminé le devoir :

---
**Avertissement** :
Ce document a été traduit à l'aide du service de traduction automatique [Co-op Translator](https://github.com/Azure/co-op-translator). Bien que nous nous efforcions d'assurer l'exactitude, veuillez noter que les traductions automatisées peuvent contenir des erreurs ou des inexactitudes. Le document original dans sa langue d'origine doit être considéré comme la source faisant autorité. Pour des informations critiques, il est recommandé de recourir à une traduction professionnelle réalisée par un humain. Nous déclinons toute responsabilité en cas de malentendus ou d'interprétations erronées résultant de l'utilisation de cette traduction.
Ce document a été traduit à l'aide du service de traduction automatique [Co-op Translator](https://github.com/Azure/co-op-translator). Bien que nous nous efforcions d'assurer l'exactitude, veuillez noter que les traductions automatiques peuvent contenir des erreurs ou des inexactitudes. Le document original dans sa langue d'origine doit être considéré comme la source faisant autorité. Pour des informations critiques, il est recommandé de recourir à une traduction humaine professionnelle. Nous ne sommes pas responsables des malentendus ou des interprétations erronées résultant de l'utilisation de cette traduction.
[שאלון לפני השיעור](https://ff-quizzes.netlify.app/web/quiz/47)
[חידון לפני ההרצאה](https://ff-quizzes.netlify.app/web/quiz/47)
### הקדמה
### מבוא
ככל שאפליקציית ווב גדלה, קשה יותר לעקוב אחר כל זרימות הנתונים. איזה קוד מקבל את הנתונים, איזה עמוד צורך אותם, איפה ומתי צריך לעדכן אותם... קל להגיע לקוד מבולגן שקשה לתחזק. זה נכון במיוחד כשצריך לשתף נתונים בין עמודים שונים באפליקציה, למשל נתוני משתמש. מושג *ניהול מצב* תמיד היה קיים בכל סוגי התוכניות, אבל ככל שאפליקציות ווב ממשיכות לגדול במורכבות, זה הפך לנקודה מרכזית שיש לחשוב עליה במהלך הפיתוח.
ככל שאפליקציית ווב גדלה, נעשה מאתגר לעקוב אחרי כל זרימות הנתונים. איזה קוד מקבל את הנתונים, איזודף צורך אותם, היכן ומתי צריך לעדכן אותם... קל מאוד להגיע לקוד מבולגן שקשה לתחזק. זה נכון במיוחד כאשר יש צורך לשתף נתונים בין דפים שונים באפליקציה, כמו נתוני משתמש. המושג של *ניהול מצב* תמיד היה קיים בכל סוגי התוכניות, אך ככל שאפליקציות ווב נעשות מורכבות יותר, זהו כעת נקודה מרכזית שיש לחשוב עליה במהלך הפיתוח.
בחלק האחרון הזה, נבחן את האפליקציה שבנינו כדי לחשוב מחדש על איך המצב מנוהל, כך שתתמוך ברענון הדפדפן בכל נקודה ותשמור נתונים בין סשנים של משתמשים.
בחלק האחרון הזה, נבחן מחדש את האפליקציה שבנינו כדי לחשוב מחדש על ניהול המצב, כך שתתמוך ברענון הדפדפן בכל נקודה ותשמור נתונים בין סשנים של משתמשים.
### דרישות מקדימות
עליך להשלים את חלק [שאיבת הנתונים](../3-data/README.md) של אפליקציית הווב עבור שיעור זה. כמו כן, עליך להתקין [Node.js](https://nodejs.org) ולהריץ את [שרת ה-API](../api/README.md) באופן מקומי כדי שתוכל לנהל נתוני חשבון.
עליך להשלים את החלק של [שליפת נתונים](../3-data/README.md) באפליקציית הווב עבור שיעור זה. כמו כן, עליך להתקין את [Node.js](https://nodejs.org) ולהריץ את [שרת ה-API](../api/README.md) באופן מקומי כדי שתוכל לנהל נתוני חשבון.
ניתן לבדוק שהשרת פועל כראוי על ידי ביצוע הפקודה הזו בטרמינל:
תוכל לבדוק שהשרת פועל כראוי על ידי ביצוע הפקודה הזו בטרמינל:
```sh
curl http://localhost:5000/api
@ -34,34 +34,34 @@ curl http://localhost:5000/api
## חשיבה מחדש על ניהול מצב
בשיעור [הקודם](../3-data/README.md), הצגנו מושג בסיסי של מצב באפליקציה שלנו עם המשתנה הגלובלי `account` שמכיל את נתוני הבנק של המשתמש המחובר כרגע. עם זאת, היישום הנוכחי שלנו מכיל כמה פגמים. נסה לרענן את העמוד כשאתה נמצא בלוח הבקרה. מה קורה?
בשיעור הקודם ([כאן](../3-data/README.md)), הצגנו מושג בסיסי של מצב באפליקציה שלנו עם המשתנה הגלובלי `account` שמכיל את נתוני הבנק של המשתמש המחובר כרגע. עם זאת, ליישום הנוכחי שלנו יש כמה פגמים. נסה לרענן את הדף כשאתה נמצא בלוח הבקרה. מה קורה?
ישנן 3 בעיות בקוד הנוכחי:
- המצב אינו נשמר, רענון הדפדפן מחזיר אותך לעמוד ההתחברות.
- ישנן פונקציות רבות שמעדכנות את המצב. ככל שהאפליקציה גדלה, זה יכול להקשות על מעקב אחר השינויים וקל לשכוח לעדכן אחת מהן.
- המצב אינו מנוקה, כך שכאשר אתה לוחץ על *התנתקות*, נתוני החשבון עדיין שם למרות שאתה נמצא בעמוד ההתחברות.
- המצב אינו נשמר, ורענון הדפדפן מחזיר אותך לדף ההתחברות.
- ישנן פונקציות רבות שמעדכנות את המצב. ככל שהאפליקציה גדלה, זה יכול להקשות על מעקב אחר השינויים וקל לשכוח לעדכן משהו.
- המצב אינו מתנקה, כך שכאשר אתה לוחץ על *התנתקות*, נתוני החשבון עדיין שם למרות שאתה בדף ההתחברות.
יכולנו לעדכן את הקוד שלנו כדי להתמודד עם הבעיות הללו אחת אחת, אבל זה היה יוצר כפילות קוד רבה ומסבך את האפליקציה יותר. או שאנחנו יכולים לעצור לכמה דקות ולחשוב מחדש על האסטרטגיה שלנו.
יכולנו לעדכן את הקוד שלנו כדי לטפל בבעיות הללו אחת אחת, אבל זה ייצור כפילויות קוד ויהפוך את האפליקציה למורכבת יותר וקשה יותר לתחזוקה. או שנוכל לעצור לכמה דקות ולחשוב מחדש על האסטרטגיה שלנו.
> אילו בעיות אנחנו באמת מנסים לפתור כאן?
[ניהול מצב](https://en.wikipedia.org/wiki/State_management) עוסק במציאת גישה טובה לפתרון שתי הבעיות הספציפיות הללו:
[ניהול מצב](https://en.wikipedia.org/wiki/State_management) עוסק במציאת גישה טובה לפתרון שתי הבעיות העיקריות הללו:
- איך לשמור על זרימות הנתונים באפליקציה מובנות?
- איך לשמור על נתוני המצב תמיד מסונכרנים עם ממשק המשתמש (ולהפך)?
- איך לשמור על נתוני המצב תמיד מסונכרנים עם ממשק המשתמש (ולהיפך)?
ברגע שתטפל בזה, כל בעיה אחרת שעשויה להיות לך עשויה כבר להיפתר או להפוך לקלה יותר לפתרון. ישנן גישות רבות לפתרון בעיות אלו, אבל נבחר פתרון נפוץ שמורכב מ**מרכזיות הנתונים והדרכים לשנות אותם**. זרימות הנתונים ייראו כך:
ברגע שטיפלת בבעיות הללו, כל בעיה אחרת עשויה כבר להיפתר או להיות קלה יותר לפתרון. ישנן גישות רבות לפתרון הבעיות הללו, אבל נבחר בפתרון נפוץ שמורכב מ**ריכוז הנתונים והדרכים לשנות אותם**. זרימות הנתונים ייראו כך:


> לא נעסוק כאן בחלק שבו הנתונים מעדכנים את התצוגה באופן אוטומטי, מכיוון שזה קשור למושגים מתקדמים יותר של [תכנות תגובתי](https://en.wikipedia.org/wiki/Reactive_programming). זה נושא מעמיק טוב אם אתה מעוניין.
> לא נעסוק כאן בחלק שבו הנתונים מעדכנים אוטומטית את התצוגה, שכן זה קשור למושגים מתקדמים יותר של [תכנות תגובתי](https://en.wikipedia.org/wiki/Reactive_programming). זהו נושא מעמיק טוב אם תרצה להעמיק.
✅ יש הרבה ספריות עם גישות שונות לניהול מצב, [Redux](https://redux.js.org) היא אפשרות פופולרית. כדאי להסתכל על המושגים והתבניות שבהם משתמשים, שכן זה לעיתים קרובות דרך טובה ללמוד על בעיות פוטנציאליות שאתה עשוי להתמודד איתן באפליקציות ווב גדולות וכיצד ניתן לפתור אותן.
✅ ישנן ספריות רבות עם גישות שונות לניהול מצב, [Redux](https://redux.js.org) היא אפשרות פופולרית. כדאי להכיר את המושגים והתבניות שבהן משתמשים, שכן זהו לעיתים קרובות דרך טובה ללמוד על בעיות פוטנציאליות שאתה עשוי להיתקל בהן באפליקציות ווב גדולות וכיצד ניתן לפתור אותן.
### משימה
נתחיל עם קצת ריפקטורינג. החלף את ההצהרה של`account`:
נתחיל עם קצת רפקטורינג. החלף את ההצהרה `account`:
```js
let account = null;
@ -75,7 +75,7 @@ let state = {
};
```
הרעיון הוא *לרכז* את כל נתוני האפליקציה שלנו באובייקט מצב יחיד. כרגע יש לנו רק `account` במצב, כך שזה לא משנה הרבה, אבל זה יוצר דרך להתפתחויות עתידיות.
הרעיון הוא *לרכז* את כל נתוני האפליקציה שלנו באובייקט מצב יחיד. כרגע יש לנו רק `account` במצב, כך שזה לא משנה הרבה, אבל זה יוצר בסיס לשינויים עתידיים.
עלינו גם לעדכן את הפונקציות שמשתמשות בו. בפונקציות `register()`ו-`login()`, החלף `account = ...` ב-`state.account = ...`;
@ -85,17 +85,17 @@ let state = {
const account = state.account;
```
הריפקטורינג הזה בפני עצמו לא הביא שיפורים רבים, אבל הרעיון היה להניח את היסודות לשינויים הבאים.
הרפקטורינג הזה בפני עצמו לא הביא שיפורים רבים, אבל הרעיון היה להניח את היסודות לשינויים הבאים.
## מעקב אחר שינויים בנתונים
עכשיו כששמנו במקום את אובייקט `state` לאחסון הנתונים שלנו, השלב הבא הוא לרכז את העדכונים. המטרה היא להקל על מעקב אחר כל שינוי ומתי הוא מתרחש.
עכשיו, כשיש לנו את אובייקט ה-`state` לאחסון הנתונים שלנו, השלב הבא הוא לרכז את העדכונים. המטרה היא להקל על המעקב אחר כל שינוי ומתי הוא מתרחש.
כדי להימנע משינויים באובייקט `state`, זה גם נוהג טוב לשקול אותו [*בלתי ניתן לשינוי*](https://en.wikipedia.org/wiki/Immutable_object), כלומר שהוא לא יכול להיות שונה כלל. זה גם אומר שאתה צריך ליצור אובייקט מצב חדש אם אתה רוצה לשנות משהו בו. על ידי כך, אתה בונה הגנה מפני [תופעות לוואי](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) לא רצויות, ופותח אפשרויות לתכונות חדשות באפליקציה שלך כמו יישום undo/redo, תוך גם הקלה על איתור באגים. למשל, תוכל לרשום כל שינוי שנעשה במצב ולשמור היסטוריה של השינויים כדי להבין את מקור הבאג.
כדי למנוע שינויים באובייקט ה-`state`, זה גם נוהג טוב לשקול אותו כ[*בלתי ניתן לשינוי*](https://en.wikipedia.org/wiki/Immutable_object), כלומר שלא ניתן לשנות אותו כלל. זה גם אומר שעליך ליצור אובייקט מצב חדש אם ברצונך לשנות משהו בו. על ידי כך, אתה בונה הגנה מפני [תופעות לוואי](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) לא רצויות, ופותח אפשרויות לתכונות חדשות באפליקציה שלך כמו יישום פעולות ביטול/חזרה, תוך הקלה על איתור באגים. לדוגמה, תוכל לרשום כל שינוי שנעשה במצב ולשמור היסטוריה של השינויים כדי להבין את מקור הבאג.
ב-JavaScript, ניתן להשתמש ב-[`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) כדי ליצור גרסה בלתי ניתנת לשינוי של אובייקט. אם תנסה לבצע שינויים באובייקט בלתי ניתן לשינוי, תתעורר חריגה.
ב-JavaScript, תוכל להשתמש ב-[`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) כדי ליצור גרסה בלתי ניתנת לשינוי של אובייקט. אם תנסה לבצע שינויים באובייקט בלתי ניתן לשינוי, תתעורר חריגה.
✅ האם אתה יודע את ההבדל בין אובייקט בלתי ניתן לשינוי *שטחי* לבין *עמוק*? תוכל לקרוא על כך [כאן](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ האם אתה יודע מה ההבדל בין אובייקט בלתי ניתן לשינוי *שטחי* לבין *עמוק*? תוכל לקרוא על כך [כאן](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### משימה
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
בפונקציה זו, אנחנו יוצרים אובייקט מצב חדש ומעתיקים נתונים מהמצב הקודם באמצעות [*אופרטור הפיזור (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). לאחר מכן אנחנו מחליפים תכונה מסוימת של אובייקט המצב עם הנתונים החדשים באמצעות [הסימון המרובע](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` להקצאה. לבסוף, אנחנו נועלים את האובייקט כדי למנוע שינויים באמצעות `Object.freeze()`. כרגע יש לנו רק את התכונה `account` במצב, אבל עם הגישה הזו ניתן להוסיף כמה תכונות שצריך במצב.
בפונקציה הזו, אנחנו יוצרים אובייקט מצב חדש ומעתיקים נתונים מהמצב הקודם באמצעות [*אופרטור הפיזור (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). לאחר מכן, אנו מחליפים מאפיין מסוים של אובייקט המצב עם הנתונים החדשים באמצעות [הערת סוגריים](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` לצורך השמה. לבסוף, אנו נועלים את האובייקט כדי למנוע שינויים באמצעות `Object.freeze()`. כרגע יש לנו רק את המאפיין `account` במצב, אבל עם הגישה הזו תוכל להוסיף כמה מאפיינים שתרצה.
נעדכן גם את אתחול המצב כדי לוודא שהמצב ההתחלתי נעול גם הוא:
נעדכן גם את האתחול של `state` כדי לוודא שהמצב ההתחלתי נעול גם הוא:
```js
let state = Object.freeze({
@ -120,19 +120,19 @@ let state = Object.freeze({
});
```
לאחר מכן, עדכן את הפונקציה `register` על ידי החלפת ההקצאה `state.account = result;` ב:
לאחר מכן, עדכן את פונקציית `register` על ידי החלפת ההשמה `state.account = result;` ב:
```js
updateState('account', result);
```
עשה את אותו הדבר עם הפונקציה `login`, החלף `state.account = data;` ב:
עשה את אותו הדבר עם פונקציית `login`, והחלף `state.account = data;` ב:
```js
updateState('account', data);
```
ננצל את ההזדמנות כדי לתקן את הבעיה של נתוני החשבון שלא מתנקים כאשר המשתמש לוחץ על *התנתקות*.
כעת ננצל את ההזדמנות לתקן את הבעיה של אי ניקוי נתוני החשבון כאשר המשתמש לוחץ על *התנתקות*.
צור פונקציה חדשה בשם `logout()`:
@ -145,47 +145,47 @@ function logout() {
ב-`updateDashboard()`, החלף את ההפניה `return navigate('/login');` ב-`return logout()`;
נסה לרשום חשבון חדש, להתנתק ולהתחבר שוב כדי לבדוק שהכל עדיין עובד כראוי.
נסה להירשם לחשבון חדש, להתנתק ולהתחבר שוב כדי לבדוק שהכול עדיין עובד כראוי.
> טיפ: תוכל להסתכל על כל השינויים במצב על ידי הוספת `console.log(state)` בתחתית `updateState()` ופתיחת הקונסול בכלי הפיתוח של הדפדפן שלך.
> טיפ: תוכל לעקוב אחרי כל השינויים במצב על ידי הוספת `console.log(state)` בתחתית `updateState()` ופתיחת הקונסולה בכלי הפיתוח של הדפדפן שלך.
## שמירת המצב
רוב אפליקציות הווב צריכות לשמור נתונים כדי לעבוד כראוי. כל הנתונים הקריטיים בדרך כלל נשמרים בבסיס נתונים ונגישים דרך שרת API, כמו נתוני החשבון של המשתמש במקרה שלנו. אבל לפעמים, זה גם מעניין לשמור נתונים באפליקציה בצד הלקוח שרצה בדפדפן, לשיפור חוויית המשתמש או לשיפור ביצועי הטעינה.
רוב אפליקציות הווב צריכות לשמור נתונים כדי לעבוד כראוי. כל הנתונים הקריטיים נשמרים בדרך כלל במסד נתונים ונגישים דרך API של שרת, כמו נתוני חשבון המשתמש במקרה שלנו. אבל לפעמים, זה גם מעניין לשמור נתונים מסוימים באפליקציה בצד הלקוח שרצה בדפדפן שלך, לשיפור חוויית המשתמש או לשיפור ביצועי הטעינה.
כשאתה רוצה לשמור נתונים בדפדפן שלך, יש כמה שאלות חשובות שכדאי לשאול את עצמך:
כשאתה רוצה לשמור נתונים בדפדפן שלך, יש כמה שאלות חשובות שעליך לשאול את עצמך:
- *האם הנתונים רגישים?*כדאי להימנע משמירת נתונים רגישים בצד הלקוח, כמו סיסמאות משתמש.
- *לכמה זמן אתה צריך לשמור את הנתונים האלה?* האם אתה מתכנן לגשת לנתונים רק עבור הסשן הנוכחי או שאתה רוצה שהם יישמרו לנצח?
- *האם הנתונים רגישים?*עליך להימנע משמירת נתונים רגישים בצד הלקוח, כמו סיסמאות משתמש.
- *לכמה זמן אתה צריך לשמור את הנתונים הללו?* האם אתה מתכנן לגשת לנתונים הללו רק במהלך הסשן הנוכחי או שאתה רוצה שהם יישמרו לנצח?
ישנן דרכים רבות לאחסן מידע בתוך אפליקציית ווב, תלוי במה שאתה רוצה להשיג. למשל, ניתן להשתמש ב-URLs כדי לשמור שאילתת חיפוש, ולהפוך אותה לשיתופית בין משתמשים. ניתן גם להשתמש ב-[עוגיות HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) אם הנתונים צריכים להיות משותפים עם השרת, כמו מידע [אימות](https://en.wikipedia.org/wiki/Authentication).
ישנן דרכים רבות לאחסן מידע בתוך אפליקציית ווב, תלוי במה שאתה רוצה להשיג. לדוגמה, תוכל להשתמש בכתובות URL כדי לשמור שאילתת חיפוש, ולהפוך אותה לשיתופית בין משתמשים. תוכל גם להשתמש ב-[עוגיות HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) אם הנתונים צריכים להיות משותפים עם השרת, כמו מידע על [אימות](https://en.wikipedia.org/wiki/Authentication).
אפשרות נוספת היא להשתמש באחת מה-APIs הרבות של הדפדפן לאחסון נתונים. שתיים מהן מעניינות במיוחד:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [חנות מפתח/ערך](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) שמאפשרת לשמור נתונים ספציפיים לאתר הנוכחי בין סשנים שונים. הנתונים שנשמרים בה לעולםלא פגים.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): זו עובדת באותו אופן כמו `localStorage` מלבד שהנתונים שנשמרים בה נמחקים כאשר הסשן מסתיים (כאשר הדפדפן נסגר).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [מאגר מפתחות/ערכים](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) שמאפשר לשמור נתונים ספציפיים לאתר הנוכחי בין סשנים שונים. הנתונים שנשמרים בו אינם פגים לעולם.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): זה עובד כמו `localStorage` פרט לכך שהנתונים שנשמרים בו נמחקים כאשר הסשן מסתיים (כאשר הדפדפן נסגר).
שימו לב ששתי ה-APIs הללו מאפשרות לשמור רק [מחרוזות](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). אם תרצה לשמור אובייקטים מורכבים, תצטרך לסדר אותם לפורמט [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) באמצעות [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
שימו לב ששני ה-APIs הללו מאפשרים לשמור רק [מחרוזות](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). אם ברצונך לשמור אובייקטים מורכבים, תצטרך לסדר אותם לפורמט [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) באמצעות [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ אם תרצה ליצור אפליקציית ווב שלא עובדת עם שרת, ניתן גם ליצור בסיס נתונים בצד הלקוח באמצעות [API של `IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). זה שמור למקרים מתקדמים או אם אתה צריך לשמור כמות משמעותית של נתונים, מכיוון שזה מורכב יותר לשימוש.
✅ אם ברצונך ליצור אפליקציית ווב שאינה עובדת עם שרת, ניתן גם ליצור מסד נתונים בצד הלקוח באמצעות [API של `IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). זה שמור למקרי שימוש מתקדמים או אם אתה צריך לשמור כמות משמעותית של נתונים, שכן זה מורכב יותר לשימוש.
### משימה
אנחנו רוצים שהמשתמשים שלנו יישארו מחוברים עד שהם לוחצים באופן מפורש על כפתור *התנתקות*, אז נשתמש ב-`localStorage` כדי לשמור את נתוני החשבון. קודם כל, נגדיר מפתח שנשתמש בו כדי לשמור את הנתונים שלנו.
אנחנו רוצים שהמשתמשים שלנו יישארו מחוברים עד שהם לוחצים במפורש על כפתור *התנתקות*, ולכן נשתמש ב-`localStorage` כדי לשמור את נתוני החשבון. ראשית, נגדיר מפתח שנשתמש בו לשמירת הנתונים שלנו.
```js
const storageKey = 'savedAccount';
```
לאחר מכן, הוסף את השורה הזו בסוף הפונקציה`updateState()`:
לאחר מכן, הוסף את השורה הזו בסוף פונקציית`updateState()`:
עם זה, נתוני החשבון של המשתמש יישמרו ותמיד יהיו מעודכנים כפי שריכזנו קודם את כל עדכוני המצב שלנו. כאן אנחנו מתחילים ליהנות מכל הריפקטורינג הקודם שלנו 🙂.
עם זה, נתוני חשבון המשתמש יישמרו ותמיד יהיו מעודכנים, כפי שריכזנו קודם את כל עדכוני המצב שלנו. כאן אנחנו מתחילים ליהנות מכל הרפקטורינג הקודם שלנו 🙂.
מכיוון שהנתונים נשמרים, עלינו גם לדאוג לשחזר אותם כאשר האפליקציה נטענת. מכיוון שאנחנו מתחילים לקבל יותר קוד אתחול, זה עשוי להיות רעיון טוב ליצור פונקציה חדשה בשם `init`, שכוללת גם את הקוד הקודם שלנו בתחתית `app.js`:
מכיוון שהנתונים נשמרים, עלינו גם לדאוג לשחזר אותם כאשר האפליקציה נטענת. מכיוון שנתחיל לקבל יותר קוד אתחול, ייתכן שזה רעיון טוב ליצור פונקציה חדשה בשם `init`, שכוללת גם את הקוד הקודם בתחתית `app.js`:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
כאן אנחנו משחזרים את הנתונים שנשמרו, ואם יש כאלה אנחנו מעדכנים את המצב בהתאם. חשוב לעשות זאת *לפני* עדכון המסלול, מכיוון שייתכן שיש קוד שמסתמך על המצב במהלך עדכון העמוד.
כאן אנו משחזרים את הנתונים שנשמרו, ואם יש כאלה אנו מעדכנים את המצב בהתאם. חשוב לעשות זאת *לפני* עדכון הנתיב, שכן ייתכן שיש קוד שמסתמך על המצב במהלך עדכון הדף.
אנחנו יכולים גם להפוך את עמוד *לוח הבקרה* לעמוד ברירת המחדל של האפליקציה שלנו, מכיוון שאנחנו עכשיו שומרים את נתוני החשבון. אם לא נמצאו נתונים, לוח הבקרה דואג להפנות לעמוד*התחברות* בכל מקרה. ב-`updateRoute()`, החלף את ברירת המחדל `return navigate('/login');` ב-`return navigate('/dashboard');`.
אנחנו גם יכולים להפוך את דף*לוח הבקרה* לדף ברירת המחדל של האפליקציה שלנו, מכיוון שעכשיו אנחנו שומרים את נתוני החשבון. אם לא נמצאו נתונים, לוח הבקרה דואג להפניה לדף*התחברות* בכל מקרה. ב-`updateRoute()`, החלף את ברירת המחדל `return navigate('/login');` ב-`return navigate('/dashboard');`.
עכשיו התחבר לאפליקציה ונסה לרענן את העמוד. אתה אמור להישאר בלוח הבקרה. עם העדכון הזה טיפלנו בכל הבעיות הראשוניות שלנו...
כעת התחבר לאפליקציה ונסה לרענן את הדף. אתה אמור להישאר בלוח הבקרה. עם העדכון הזה טיפלנו בכל הבעיות הראשוניות שלנו...
## רענון הנתונים
...אבל ייתכן שגם יצרנו בעיה חדשה. אופס!
עבור ללוח הבקרה באמצעות החשבון `test`, ואז הרץ את הפקודה הזו בטרמינל כדי ליצור עסקה חדשה:
עבור ללוח הבקרה באמצעות חשבון `test`, ואז הרץ את הפקודה הזו בטרמינל כדי ליצור עסקה חדשה:
נסה לרענן את עמוד לוח הבקרה בדפדפן עכשיו. מה קורה? האם אתה רואה את העסקה החדשה?
נסה לרענן את דף לוח הבקרה בדפדפן עכשיו. מה קורה? האם אתה רואה את העסקה החדשה?
המצב נשמר ללא הגבלה בזכות ה-`localStorage`, אבל זה גם אומר שהוא לעולם לא מתעדכן עד שתתנתק מהאפליקציה ותתחבר שוב!
המצב נשמר ללא הגבלת זמן בזכות ה-`localStorage`, אבל זה גם אומר שהוא אף פעם לא מתעדכן עד שאתה מתנתק מהאפליקציה ומתחבר שוב!
אסטרטגיה אפשרית לתקן זאת היא לטעון מחדש את נתוני החשבון בכל פעם שלוח הבקרה נטען, כדי להימנע מנתונים מיושנים.
@ -258,7 +258,7 @@ async function refresh() {
}
```
זו מעדכנת את נתוני החשבון, ואז דואגת לעדכן את ה-HTML של עמוד לוח הבקרה. זה מה שאנחנו צריכים לקרוא כאשר מסלול לוח הבקרה נטען. עדכן את הגדרת המסלול עם:
פונקציה זו מעדכנת את נתוני החשבון, ואז דואגת לעדכן את ה-HTML של דף לוח הבקרה. זה מה שאנחנו צריכים לקרוא כאשר נתיב לוח הבקרה נטען. עדכן את הגדרת הנתיב עם:
```js
const routes = {
@ -273,22 +273,22 @@ const routes = {
## 🚀 אתגר
עכשיו כשאנחנו טוענים מחדש את נתוני החשבון בכל פעם שלוח הבקרה נטען, האם לדעתך אנחנו עדיין צריכים לשמור *את כל נתוני החשבון*?
עכשיו, כשאנחנו טוענים מחדש את נתוני החשבון בכל פעם שלוח הבקרה נטען, האם לדעתך אנחנו עדיין צריכים לשמור *את כל נתוני החשבון*?
נסה לעבוד יחד כדי לשנות מה נשמר ומה נטען מ-`localStorage` כך שיכלול רק את מה שדרוש באופן מוחלט כדי שהאפליקציה תעבוד.
נסה לעבוד יחד כדי לשנות מה נשמר ומה נטען מ-`localStorage` כך שיכלול רק את מה שחיוני לאפליקציה לעבוד.
## שאלון אחרי השיעור
[שאלון אחרי השיעור](https://ff-quizzes.netlify.app/web/quiz/48)
## חידון לאחר ההרצאה
[שאלון לאחר ההרצאה](https://ff-quizzes.netlify.app/web/quiz/48)
## משימה
[מימוש דיאלוג "הוסף עסקה"](assignment.md)
הנה דוגמה לתוצאה לאחר השלמת המשימה:
[מימוש דיאלוג "הוספת עסקה"](assignment.md)
להלן דוגמה לתוצאה לאחר השלמת המשימה:


---
**כתב ויתור**:
מסמך זה תורגם באמצעות שירות תרגום מבוסס בינה מלאכותית [Co-op Translator](https://github.com/Azure/co-op-translator). למרות שאנו שואפים לדיוק, יש לקחת בחשבון שתרגומים אוטומטיים עשויים להכיל שגיאות או אי-דיוקים. המסמך המקורי בשפתו המקורית נחשב למקור הסמכותי. למידע קריטי, מומלץ להשתמש בתרגום מקצועי על ידי בני אדם. איננו נושאים באחריות לכל אי-הבנה או פרשנות שגויה הנובעת משימוש בתרגום זה.
מסמך זה תורגם באמצעות שירות תרגום מבוסס בינה מלאכותית [Co-op Translator](https://github.com/Azure/co-op-translator). למרות שאנו שואפים לדיוק, יש לקחת בחשבון שתרגומים אוטומטיים עשויים להכיל שגיאות או אי דיוקים. המסמך המקורי בשפתו המקורית צריך להיחשב כמקור סמכותי. עבור מידע קריטי, מומלץ להשתמש בתרגום מקצועי על ידי אדם. איננו נושאים באחריות לאי הבנות או לפרשנויות שגויות הנובעות משימוש בתרגום זה.
जैसे-जैसे एक वेब एप्लिकेशन बढ़ता है, डेटा फ्लो को ट्रैक करना चुनौतीपूर्ण हो जाता है। कौन सा कोड डेटा प्राप्त करता है, कौन सा पेज इसे उपयोग करता है, इसे कब और कहां अपडेट करने की आवश्यकता है... यह सब गड़बड़ कोड में बदल सकता है जिसे बनाए रखना मुश्किल हो जाता है। यह समस्या तब और बढ़ जाती है जब आपको अपने ऐप के विभिन्न पेजों के बीच डेटा साझा करना होता है, जैसे उपयोगकर्ता डेटा। *स्टेट मैनेजमेंट* की अवधारणा हमेशा से सभी प्रकार के प्रोग्रामों में मौजूद रही है, लेकिन जैसे-जैसे वेब ऐप्स की जटिलता बढ़ती जा रही है, यह विकास के दौरान विचार करने का एक महत्वपूर्ण बिंदु बन गया है।
जैसे-जैसे वेब एप्लिकेशन बढ़ता है, डेटा प्रवाह को ट्रैक करना चुनौतीपूर्ण हो जाता है। कौन सा कोड डेटा प्राप्त करता है, कौन सा पेज इसे उपयोग करता है, इसे कब और कहां अपडेट करने की आवश्यकता है... यह आसानी से गड़बड़ कोड में बदल सकता है जिसे बनाए रखना मुश्किल है। यह विशेष रूप से तब सच है जब आपको अपने ऐप के विभिन्न पेजों के बीच डेटा साझा करने की आवश्यकता होती है, जैसे उपयोगकर्ता डेटा। *स्टेट मैनेजमेंट* की अवधारणा हमेशा सभी प्रकार के प्रोग्रामों में मौजूद रही है, लेकिन जैसे-जैसे वेब ऐप्स की जटिलता बढ़ती जा रही है, यह अब विकास के दौरान विचार करने का एक प्रमुख बिंदु बन गया है।
इस अंतिम भाग में, हम उस ऐप पर फिर से विचार करेंगे जिसे हमने बनाया है, ताकि स्टेट को बेहतर तरीके से प्रबंधित किया जा सके, ब्राउज़र रिफ्रेश को किसी भी समय सपोर्ट किया जा सके, और उपयोगकर्ता सत्रों के बीच डेटा को बनाए रखा जा सके।
इस अंतिम भाग में, हम उस ऐप पर पुनर्विचार करेंगे जिसे हमने बनाया है ताकि स्टेट को बेहतर तरीके से प्रबंधित किया जा सके, जिससे ब्राउज़र को किसी भी समय रिफ्रेश करने का समर्थन मिले और उपयोगकर्ता सत्रों के बीच डेटा को बनाए रखा जा सके।
### पूर्वापेक्षा
आपको इस पाठ के लिए वेब ऐप के [डेटा फेचिंग](../3-data/README.md) भाग को पूरा कर लेना चाहिए। आपको [Node.js](https://nodejs.org) को इंस्टॉल करना होगा और [सर्वर API](../api/README.md) को लोकली चलाना होगा ताकि आप खाता डेटा प्रबंधित कर सकें।
आपको इस पाठ के लिए वेब ऐप के [डेटा फेचिंग](../3-data/README.md) भाग को पूरा करना होगा। आपको [Node.js](https://nodejs.org) इंस्टॉल करना होगा और [सर्वर API](../api/README.md) को लोकल रूप से चलाना होगा ताकि आप अकाउंट डेटा को प्रबंधित कर सकें।
आप यह कमांड टर्मिनल में चलाकर सुनिश्चित कर सकते हैं कि सर्वर सही तरीके से चल रहा है:
आप यह कमांड टर्मिनल में चलाकर जांच सकते हैं कि सर्वर सही तरीके से चल रहा है:
```sh
curl http://localhost:5000/api
@ -32,36 +32,36 @@ curl http://localhost:5000/api
---
## स्टेट मैनेजमेंट पर पुनर्विचार
## स्टेट मैनेजमेंट पर पुनर्विचार करें
[पिछले पाठ](../3-data/README.md) में, हमने अपने ऐप में स्टेट की एक बुनियादी अवधारणा पेश की थी, जिसमें `account` नामक एक ग्लोबल वेरिएबल था जो वर्तमान में लॉग इन किए गए उपयोगकर्ता के बैंक डेटा को संग्रहीत करता था। हालांकि, हमारे वर्तमान कार्यान्वयन में कुछ खामियां हैं। डैशबोर्ड पर रहते हुए पेज को रिफ्रेश करने का प्रयास करें। क्या होता है?
[पिछले पाठ](../3-data/README.md) में, हमने अपने ऐप में स्टेट की एक बुनियादी अवधारणा पेश की थी जिसमें `account` नामक एक ग्लोबल वेरिएबल था जो वर्तमान में लॉग इन किए गए उपयोगकर्ता के बैंक डेटा को संग्रहीत करता है। हालांकि, हमारे वर्तमान कार्यान्वयन में कुछ खामियां हैं। डैशबोर्ड पर रहते हुए पेज को रिफ्रेश करने का प्रयास करें। क्या होता है?
वर्तमान कोड में 3 समस्याएं हैं:
- स्टेट को संरक्षित नहीं किया गया है, क्योंकि ब्राउज़र रिफ्रेश आपको लॉगिन पेज पर वापस ले जाता है।
- स्टेट को संशोधित करने वाले कई फंक्शन हैं। जैसे-जैसे ऐप बढ़ता है, यह परिवर्तनों को ट्रैक करना कठिन बना सकता है और किसी एक को अपडेट करना भूलना आसान हो सकता है।
- स्टेट को साफ नहीं किया गया है, इसलिए जब आप *लॉगआउट* पर क्लिक करते हैं, तो खाता डेटा अभी भी वहां होता है, भले ही आप लॉगिन पेज पर हों।
- स्टेट को बनाए नहीं रखा गया है, क्योंकि ब्राउज़र रिफ्रेश आपको लॉगिन पेज पर वापस ले जाता है।
- स्टेट को संशोधित करने वाले कई फंक्शन हैं। जैसे-जैसे ऐप बढ़ता है, यह परिवर्तनों को ट्रैक करना मुश्किल बना सकता है और एक को अपडेट करना भूलना आसान हो जाता है।
- स्टेट को साफ नहीं किया गया है, इसलिए जब आप *लॉगआउट* पर क्लिक करते हैं तो अकाउंट डेटा अभी भी वहां होता है, भले ही आप लॉगिन पेज पर हों।
हम इन समस्याओं को एक-एक करके हल करने के लिए अपने कोड को अपडेट कर सकते हैं, लेकिन इससे कोड डुप्लिकेशन बढ़ेगा और ऐप अधिक जटिल और बनाए रखने में कठिन हो जाएगा। या हम कुछ मिनटों के लिए रुक सकते हैं और अपनी रणनीति पर पुनर्विचार कर सकते हैं।
हम इन समस्याओं को एक-एक करके हल करने के लिए अपने कोड को अपडेट कर सकते हैं, लेकिन इससे कोड का अधिक डुप्लीकेशन होगा और ऐप अधिक जटिल और बनाए रखने में कठिन हो जाएगा। या हम कुछ मिनटों के लिए रुक सकते हैं और अपनी रणनीति पर पुनर्विचार कर सकते हैं।
> हम वास्तव में यहां कौन सी समस्याओं को हल करने की कोशिश कर रहे हैं?
> यहां हम वास्तव में किन समस्याओं को हल करने की कोशिश कर रहे हैं?
[स्टेट मैनेजमेंट](https://en.wikipedia.org/wiki/State_management) का उद्देश्य इन दो विशेष समस्याओं को हल करने के लिए एक अच्छा दृष्टिकोण खोजना है:
- ऐप में डेटा फ्लो को समझने योग्य कैसे रखा जाए?
- स्टेट डेटा को हमेशा उपयोगकर्ता इंटरफ़ेस के साथ (और इसके विपरीत) सिंक में कैसे रखा जाए?
- ऐप में डेटा प्रवाह को समझने योग्य कैसे रखें?
- स्टेट डेटा को हमेशा उपयोगकर्ता इंटरफ़ेस के साथ (और इसके विपरीत) सिंक में कैसे रखें?
एक बार जब आप इनका ध्यान रख लेते हैं, तो आपके पास मौजूद कोई अन्य समस्या या तो पहले ही हल हो चुकी होगी या उसे हल करना आसान हो गया होगा। इन समस्याओं को हल करने के लिए कई संभावित दृष्टिकोण हैं, लेकिन हम एक सामान्य समाधान अपनाएंगे जिसमें **डेटा और इसे बदलने के तरीकों को केंद्रीकृत करना** शामिल है। डेटा फ्लो इस प्रकार होगा:
एक बार जब आप इनका ध्यान रख लेते हैं, तो आपके पास जो भी अन्य समस्याएं हो सकती हैं, वे या तो पहले ही ठीक हो चुकी होंगी या उन्हें ठीक करना आसान हो गया होगा। इन समस्याओं को हल करने के लिए कई संभावित दृष्टिकोण हैं, लेकिन हम एक सामान्य समाधान अपनाएंगे जिसमें **डेटा और इसे बदलने के तरीकों को केंद्रीकृत करना** शामिल है। डेटा प्रवाह इस प्रकार होगा:


> यहां हम उस हिस्से को कवर नहीं करेंगे जहां डेटा स्वचालित रूप से व्यू अपडेट को ट्रिगर करता है, क्योंकि यह [रिएक्टिव प्रोग्रामिंग](https://en.wikipedia.org/wiki/Reactive_programming) की अधिक उन्नत अवधारणाओं से जुड़ा हुआ है। यदि आप गहराई से अध्ययन करना चाहते हैं, तो यह एक अच्छा विषय है।
> यहां हम उस हिस्से को कवर नहीं करेंगे जहां डेटा स्वचालित रूप से व्यू अपडेट को ट्रिगर करता है, क्योंकि यह [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming) के अधिक उन्नत अवधारणाओं से जुड़ा हुआ है। यदि आप गहराई से जानने के इच्छुक हैं, तो यह एक अच्छा विषय है।
✅ स्टेट मैनेजमेंट के लिए कई पुस्तकालय उपलब्ध हैं, [Redux](https://redux.js.org) एक लोकप्रिय विकल्प है। इसके उपयोग किए गए अवधारणाओं और पैटर्न को देखें, क्योंकि यह अक्सर यह समझने का एक अच्छा तरीका है कि बड़े वेब ऐप्स में आपको किन संभावित समस्याओं का सामना करना पड़ सकता है और उन्हें कैसे हल किया जा सकता है।
✅ स्टेट मैनेजमेंट के लिए कई लाइब्रेरी उपलब्ध हैं, जिनमें [Redux](https://redux.js.org) एक लोकप्रिय विकल्प है। इसके द्वारा उपयोग किए जाने वाले अवधारणाओं और पैटर्न को देखें क्योंकि यह अक्सर यह समझने का एक अच्छा तरीका होता है कि बड़े वेब ऐप्स में आप किन संभावित समस्याओं का सामना कर सकते हैं और उन्हें कैसे हल किया जा सकता है।
### कार्य
हम थोड़े से रिफैक्टरिंग के साथ शुरुआत करेंगे। `account` डिक्लेरेशन को बदलें:
हम थोड़ा रिफैक्टरिंग से शुरुआत करेंगे। `account` डिक्लेरेशन को बदलें:
```js
let account = null;
@ -75,7 +75,7 @@ let state = {
};
```
इसका विचार हमारे ऐप के सभी डेटा को एक सिंगल स्टेट ऑब्जेक्ट में *केंद्रीकृत* करना है। अभी के लिए हमारे पास केवल `account` है, इसलिए यह ज्यादा नहीं बदलता है, लेकिन यह भविष्य के लिए रास्ता बनाता है।
यह विचार है कि हमारे ऐप डेटा को एक सिंगल स्टेट ऑब्जेक्ट में *केंद्रीकृत* किया जाए। फिलहाल हमारे पास स्टेट में केवल `account` है, इसलिए यह ज्यादा नहीं बदलता है, लेकिन यह विकास के लिए एक रास्ता बनाता है।
हमें इसका उपयोग करने वाले फंक्शन को भी अपडेट करना होगा। `register()` और `login()` फंक्शन में, `account = ...` को `state.account = ...` से बदलें;
@ -85,21 +85,21 @@ let state = {
const account = state.account;
```
यह रिफैक्टरिंग अपने आप में ज्यादा सुधार नहीं लाती है, लेकिन इसका उद्देश्य अगले परिवर्तनों के लिए नींव रखना था।
यह रिफैक्टरिंग अपने आप में बहुत सुधार नहीं लाती है, लेकिन विचार अगले परिवर्तनों के लिए नींव रखना था।
## डेटा परिवर्तनों को ट्रैक करें
अब जब हमने डेटा संग्रहीत करने के लिए `state` ऑब्जेक्ट को स्थापित कर लिया है, तो अगला कदम अपडेट को केंद्रीकृत करना है। उद्देश्य यह है कि किसी भी परिवर्तन और उनके होने के समय को ट्रैक करना आसान हो जाए।
`state` ऑब्जेक्ट में परिवर्तन करने से बचने के लिए, इसे [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) मानना भी एक अच्छा अभ्यास है, जिसका अर्थ है कि इसे बिल्कुल भी संशोधित नहीं किया जा सकता। इसका मतलब यह भी है कि यदि आप इसमें कुछ भी बदलना चाहते हैं, तो आपको एक नया स्टेट ऑब्जेक्ट बनाना होगा। ऐसा करके, आप संभावित अवांछित [साइड इफेक्ट्स](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) से सुरक्षा बनाते हैं और अपने ऐप में नई सुविधाओं को लागू करने की संभावनाएं खोलते हैं, जैसे कि undo/redo को लागू करना, साथ ही इसे डिबग करना आसान बनाते हैं। उदाहरण के लिए, आप स्टेट में किए गए हर परिवर्तन को लॉग कर सकते हैं और बग के स्रोत को समझने के लिए परिवर्तनों का इतिहास रख सकते हैं।
`state` ऑब्जेक्ट में परिवर्तन करने से बचने के लिए, इसे [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) मानना भी एक अच्छा अभ्यास है, जिसका अर्थ है कि इसे बिल्कुल भी संशोधित नहीं किया जा सकता। इसका मतलब यह भी है कि यदि आप इसमें कुछ भी बदलना चाहते हैं तो आपको एक नया स्टेट ऑब्जेक्ट बनाना होगा। ऐसा करके, आप संभावित अवांछित [साइड इफेक्ट्स](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) के बारे में सुरक्षा बनाते हैं, और अपने ऐप में नई सुविधाओं को लागू करने जैसे कि undo/redo को सक्षम करते हैं, साथ ही इसे डिबग करना भी आसान बनाते हैं। उदाहरण के लिए, आप स्टेट में किए गए हर परिवर्तन को लॉग कर सकते हैं और परिवर्तनों का इतिहास रख सकते हैं ताकि बग के स्रोत को समझा जा सके।
जावास्क्रिप्ट में, आप [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) का उपयोग करके किसी ऑब्जेक्ट का अपरिवर्तनीय संस्करण बना सकते हैं। यदि आप अपरिवर्तनीय ऑब्जेक्ट में परिवर्तन करने का प्रयास करते हैं, तो एक अपवाद उत्पन्न होगा।
जावास्क्रिप्ट में, आप [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) का उपयोग करके ऑब्जेक्ट का एक immutable संस्करण बना सकते हैं। यदि आप immutable ऑब्जेक्ट में परिवर्तन करने का प्रयास करते हैं, तो एक अपवाद उत्पन्न होगा।
✅ क्या आप जानते हैं कि *शैलो* और *डीप* अपरिवर्तनीय ऑब्जेक्ट में क्या अंतर है? आप इसके बारे में [यहां](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) पढ़ सकते हैं।
✅ क्या आप जानते हैं कि *shallow* और *deep* immutable ऑब्जेक्ट में क्या अंतर है? आप इसके बारे में [यहां](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) पढ़ सकते हैं।
### कार्य
एक नया `updateState()` फंक्शन बनाएं:
आइए एक नया `updateState()` फंक्शन बनाएं:
```js
function updateState(property, newData) {
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
इस फंक्शन में, हम एक नया स्टेट ऑब्जेक्ट बना रहे हैं और पिछले स्टेट से डेटा को [*स्प्रेड (`...`) ऑपरेटर*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) का उपयोग करके कॉपी कर रहे हैं। फिर हम ब्रैकेट नोटेशन `[property]` का उपयोग करके स्टेट ऑब्जेक्ट की एक विशेष प्रॉपर्टी को नए डेटा के साथ ओवरराइड करते हैं। अंत में, हम `Object.freeze()` का उपयोग करके ऑब्जेक्ट को लॉक कर देते हैं ताकि उसमें संशोधन न किया जा सके। अभी के लिए, हमारे पास स्टेट में केवल `account` प्रॉपर्टी है, लेकिन इस दृष्टिकोण के साथ आप स्टेट में जितनी चाहें उतनी प्रॉपर्टी जोड़ सकते हैं।
इस फंक्शन में, हम एक नया स्टेट ऑब्जेक्ट बना रहे हैं और पिछले स्टेट से डेटा को [*स्प्रेड (`...`) ऑपरेटर*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) का उपयोग करके कॉपी कर रहे हैं। फिर हम स्टेट ऑब्जेक्ट की एक विशेष प्रॉपर्टी को [ब्रैकेट नोटेशन](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties)`[property]` का उपयोग करके नए डेटा के साथ ओवरराइड करते हैं। अंत में, हम `Object.freeze()` का उपयोग करके ऑब्जेक्ट को लॉक कर देते हैं ताकि संशोधन न हो सके। फिलहाल हमारे स्टेट में केवल `account` प्रॉपर्टी संग्रहीत है, लेकिन इस दृष्टिकोण के साथ आप स्टेट में जितनी चाहें उतनी प्रॉपर्टी जोड़ सकते हैं।
हम यह सुनिश्चित करने के लिए `state` इनिशियलाइज़ेशन को भी अपडेट करेंगे कि प्रारंभिक स्टेट भी फ्रीज हो:
अब हम *लॉगआउट* पर क्लिक करने पर खाता डेटा साफ न होने की समस्या को ठीक करने का अवसर लेंगे।
अब हम *लॉगआउट* पर क्लिक करने पर अकाउंट डेटा को साफ न करने की समस्या को ठीक करने का अवसर लेंगे।
एक नया फंक्शन `logout()` बनाएं:
@ -143,152 +143,30 @@ function logout() {
}
```
`updateDashboard()` में, `return navigate('/login');` रीडायरेक्शन को `return logout();` से बदलें;
`updateDashboard()` में, रीडायरेक्शन `return navigate('/login');` को `return logout();` से बदलें;
एक नया खाता पंजीकृत करने, लॉगआउट करने और फिर से लॉगिन करने का प्रयास करें ताकि यह जांचा जा सके कि सब कुछ अभी भी सही ढंग से काम कर रहा है।
एक नया अकाउंट रजिस्टर करें, लॉगआउट करें और फिर से लॉगिन करें ताकि यह सुनिश्चित हो सके कि सब कुछ सही तरीके से काम कर रहा है।
> टिप: आप `updateState()` के नीचे `console.log(state)` जोड़कर और अपने ब्राउज़र के डेवलपमेंट टूल्स में कंसोल खोलकर सभी स्टेट परिवर्तनों को देख सकते हैं।
> टिप: आप ब्राउज़र के डेवलपमेंट टूल्स में कंसोल खोलकर और `updateState()` के नीचे `console.log(state)` जोड़कर सभी स्टेट परिवर्तनों को देख सकते हैं।
## स्टेट को संरक्षित करें
## स्टेट को बनाए रखें
अधिकांश वेब ऐप्स को सही ढंग से काम करने के लिए डेटा को संरक्षित करने की आवश्यकता होती है। सभी महत्वपूर्ण डेटा आमतौर पर डेटाबेस में संग्रहीत किया जाता है और सर्वर API के माध्यम से एक्सेस किया जाता है, जैसे कि हमारे मामले में उपयोगकर्ता खाता डेटा। लेकिन कभी-कभी, उपयोगकर्ता अनुभव को बेहतर बनाने या लोडिंग प्रदर्शन में सुधार करने के लिए ब्राउज़र में चल रहे क्लाइंट ऐप पर कुछ डेटा संरक्षित करना भी दिलचस्प होता है।
अधिकांश वेब ऐप्स को सही तरीके से काम करने के लिए डेटा को बनाए रखने की आवश्यकता होती है। सभी महत्वपूर्ण डेटा आमतौर पर डेटाबेस में संग्रहीत किया जाता है और सर्वर API के माध्यम से एक्सेस किया जाता है, जैसे कि हमारे मामले में उपयोगकर्ता अकाउंट डेटा। लेकिन कभी-कभी, उपयोगकर्ता अनुभव को बेहतर बनाने या लोडिंग प्रदर्शन को सुधारने के लिए ब्राउज़र में चल रहे क्लाइंट ऐप पर कुछ डेटा को बनाए रखना भी दिलचस्प होता है।
जब आप अपने ब्राउज़र में डेटा संरक्षित करना चाहते हैं, तो आपको कुछ महत्वपूर्ण प्रश्न पूछने चाहिए:
- *क्या डेटा संवेदनशील है?* आपको क्लाइंट पर कोई भी संवेदनशील डेटा संग्रहीत करने से बचना चाहिए, जैसे उपयोगकर्ता पासवर्ड।
- *आपको इस डेटा को कितने समय तक रखना है?* क्या आप इस डेटा को केवल वर्तमान सत्र के लिए एक्सेस करने की योजना बना रहे हैं या आप इसे हमेशा के लिए संग्रहीत करना चाहते हैं?
वेब ऐप के अंदर जानकारी संग्रहीत करने के कई तरीके हैं, यह इस बात पर निर्भर करता है कि आप क्या हासिल करना चाहते हैं। उदाहरण के लिए, आप एक खोज क्वेरी को संग्रहीत करने के लिए URL का उपयोग कर सकते हैं और इसे उपयोगकर्ताओं के बीच साझा कर सकते हैं। यदि डेटा को सर्वर के साथ साझा करने की आवश्यकता है, जैसे [प्रमाणीकरण](https://en.wikipedia.org/wiki/Authentication) जानकारी, तो आप [HTTP कुकीज़](https://developer.mozilla.org/docs/Web/HTTP/Cookies) का उपयोग कर सकते हैं।
एक अन्य विकल्प डेटा संग्रहीत करने के लिए कई ब्राउज़र API में से एक का उपयोग करना है। इनमें से दो विशेष रूप से दिलचस्प हैं:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): एक [की/वैल्यू स्टोर](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) जो विभिन्न सत्रों में वर्तमान वेबसाइट के लिए विशिष्ट डेटा को संरक्षित करने की अनुमति देता है। इसमें संग्रहीत डेटा कभी समाप्त नहीं होता।
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): यह `localStorage` के समान काम करता है, सिवाय इसके कि इसमें संग्रहीत डेटा सत्र समाप्त होने पर (जब ब्राउज़र बंद हो जाता है) साफ हो जाता है।
ध्यान दें कि ये दोनों API केवल [स्ट्रिंग्स](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) को संग्रहीत करने की अनुमति देते हैं। यदि आप जटिल ऑब्जेक्ट संग्रहीत करना चाहते हैं, तो आपको इसे [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) प्रारूप में सीरियलाइज़ करना होगा, [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) का उपयोग करके।
✅ यदि आप एक ऐसा वेब ऐप बनाना चाहते हैं जो सर्वर के साथ काम न करे, तो [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) का उपयोग करके क्लाइंट पर एक डेटाबेस बनाना भी संभव है। यह उन्नत उपयोग के मामलों के लिए आरक्षित है या यदि आपको बड़ी मात्रा में डेटा संग्रहीत करने की आवश्यकता है, क्योंकि इसका उपयोग करना अधिक जटिल है।
### कार्य
हम चाहते हैं कि हमारे उपयोगकर्ता तब तक लॉग इन रहें जब तक वे स्पष्ट रूप से *लॉगआउट* बटन पर क्लिक न करें, इसलिए हम `localStorage` का उपयोग करके खाता डेटा संग्रहीत करेंगे। पहले, आइए एक कुंजी परिभाषित करें जिसका उपयोग हम अपने डेटा को संग्रहीत करने के लिए करेंगे।
```js
const storageKey = 'savedAccount';
```
फिर `updateState()` फंक्शन के अंत में यह लाइन जोड़ें:
इसके साथ, उपयोगकर्ता खाता डेटा संरक्षित रहेगा और हमेशा अपडेट रहेगा क्योंकि हमने पहले सभी स्टेट अपडेट को केंद्रीकृत किया था। यही वह जगह है जहां हम अपने पिछले सभी रिफैक्टरिंग का लाभ उठाना शुरू करते हैं 🙂।
चूंकि डेटा सहेजा गया है, हमें ऐप लोड होने पर इसे पुनर्स्थापित करने का भी ध्यान रखना होगा। चूंकि अब हमारे पास अधिक इनिशियलाइज़ेशन कोड होगा, इसलिए एक नया `init` फंक्शन बनाना एक अच्छा विचार हो सकता है, जिसमें `app.js` के नीचे हमारा पिछला कोड भी शामिल हो:
यहां हम सहेजे गए डेटा को पुनः प्राप्त करते हैं, और यदि कोई डेटा है, तो हम स्टेट को तदनुसार अपडेट करते हैं। यह पेज अपडेट के दौरान स्टेट पर निर्भर कोड हो सकता है, इसलिए यह *रूट अपडेट करने से पहले* करना महत्वपूर्ण है।
हम *डैशबोर्ड* पेज को अपने एप्लिकेशन का डिफ़ॉल्ट पेज भी बना सकते हैं, क्योंकि अब हम खाता डेटा संरक्षित कर रहे हैं। यदि कोई डेटा नहीं मिलता है, तो डैशबोर्ड वैसे भी *लॉगिन* पेज पर रीडायरेक्ट करता है। `updateRoute()` में, फॉलबैक `return navigate('/login');` को `return navigate('/dashboard');` से बदलें।
अब ऐप में लॉगिन करें और पेज को रिफ्रेश करने का प्रयास करें। आपको डैशबोर्ड पर ही रहना चाहिए। इस अपडेट के साथ हमने अपनी सभी प्रारंभिक समस्याओं का ध्यान रखा है...
## डेटा को रिफ्रेश करें
...लेकिन हमने शायद एक नई समस्या भी पैदा कर दी है। ओह!
`test` खाते का उपयोग करके डैशबोर्ड पर जाएं, फिर एक नया लेन-देन बनाने के लिए टर्मिनल पर यह कमांड चलाएं:
अब ब्राउज़र में डैशबोर्ड पेज को रिफ्रेश करने का प्रयास करें। क्या होता है? क्या आप नया लेन-देन देखते हैं?
स्टेट को `localStorage` के माध्यम से अनिश्चित काल तक संरक्षित किया गया है, लेकिन इसका मतलब यह भी है कि यह तब तक अपडेट नहीं होता जब तक आप ऐप से लॉग आउट और फिर से लॉग इन नहीं करते!
इस समस्या को हल करने की एक संभावित रणनीति यह है कि डैशबोर्ड लोड होने पर हर बार खाता डेटा को पुनः लोड किया जाए, ताकि डेटा पुराना न हो।
### कार्य
एक नया फंक्शन `updateAccountData` बनाएं:
```js
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` नाम से बनाएं:
```js
async function refresh() {
await updateAccountData();
updateDashboard();
}
```
यह खाता डेटा को अपडेट करता है, फिर डैशबोर्ड पेज के HTML को अपडेट करने का ध्यान रखता है। यह वही है जिसे हमें डैशबोर्ड रूट लोड होने पर कॉल करने की आवश्यकता है। रूट परिभाषा को अपडेट करें:
[लेन-देन जोड़ने का संवाद लागू करें](assignment.md)
यहां एक उदाहरण परिणाम है जो असाइनमेंट पूरा करने के बाद दिखता है:
["Add transaction" डायलॉग लागू करें](assignment.md)
यहां असाइनमेंट पूरा करने के बाद का एक उदाहरण परिणाम दिया गया है:


---
**अस्वीकरण**:
यह दस्तावेज़ AI अनुवाद सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) का उपयोग करके अनुवादित किया गया है। जबकि हम सटीकता सुनिश्चित करने का प्रयास करते हैं, कृपया ध्यान दें कि स्वचालित अनुवाद में त्रुटियां या अशुद्धियां हो सकती हैं। मूल भाषा में उपलब्ध मूल दस्तावेज़ को आधिकारिक स्रोत माना जाना चाहिए। महत्वपूर्ण जानकारी के लिए, पेशेवर मानव अनुवाद की सिफारिश की जाती है। इस अनुवाद के उपयोग से उत्पन्न किसी भी गलतफहमी या गलत व्याख्या के लिए हम उत्तरदायी नहीं हैं।
यह दस्तावेज़ AI अनुवाद सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) का उपयोग करके अनुवादित किया गया है। जबकि हम सटीकता के लिए प्रयासरत हैं, कृपया ध्यान दें कि स्वचालित अनुवादों में त्रुटियां या अशुद्धियां हो सकती हैं। मूल भाषा में उपलब्ध मूल दस्तावेज़ को प्रामाणिक स्रोत माना जाना चाहिए। महत्वपूर्ण जानकारी के लिए, पेशेवर मानव अनुवाद की सिफारिश की जाती है। इस अनुवाद के उपयोग से उत्पन्न किसी भी गलतफहमी या गलत व्याख्या के लिए हम उत्तरदायी नहीं हैं।
Kako web aplikacija raste, postaje izazovno pratiti sve tokove podataka. Koji kod dobiva podatke, koja stranica ih koristi, gdje i kada ih treba ažurirati... lako je završiti s neurednim kodom koji je teško održavati. Ovo je posebno istinito kada trebate dijeliti podatke između različitih stranica vaše aplikacije, primjerice korisničke podatke. Koncept *upravljanja stanjem* oduvijek je postojao u svim vrstama programa, ali kako web aplikacije postaju sve složenije, sada je ključno razmišljati o tome tijekom razvoja.
Kako web aplikacija raste, postaje izazov pratiti sve tokove podataka. Koji kod dobiva podatke, koja stranica ih koristi, gdje i kada ih treba ažurirati... lako je završiti s neurednim kodom koji je teško održavati. To je posebno istinito kada trebate dijeliti podatke između različitih stranica aplikacije, primjerice korisničke podatke. Koncept *upravljanja stanjem* oduvijek je postojao u svim vrstama programa, ali kako web aplikacije postaju sve složenije, sada je ključno razmišljati o tome tijekom razvoja.
U ovom završnom dijelu, pregledat ćemo aplikaciju koju smo izgradili kako bismo preispitali način upravljanja stanjem, omogućujući podršku za osvježavanje preglednika u bilo kojem trenutku i zadržavanje podataka tijekom korisničkih sesija.
U ovom završnom dijelu, pregledat ćemo aplikaciju koju smo izgradili kako bismo ponovno razmotrili način upravljanja stanjem, omogućujući podršku za osvježavanje preglednika u bilo kojem trenutku i trajno čuvanje podataka tijekom korisničkih sesija.
### Preduvjeti
Potrebno je da ste završili dio [dohvaćanja podataka](../3-data/README.md) za ovu lekciju. Također trebate instalirati [Node.js](https://nodejs.org) i [pokrenuti API poslužitelj](../api/README.md) lokalno kako biste mogli upravljati podacima računa.
Potrebno je da ste završili dio aplikacije za [dohvaćanje podataka](../3-data/README.md) za ovu lekciju. Također trebate instalirati [Node.js](https://nodejs.org) i [pokrenuti API poslužitelj](../api/README.md) lokalno kako biste mogli upravljati podacima o računima.
Možete testirati radi li poslužitelj ispravno izvršavanjem ove naredbe u terminalu:
@ -32,32 +32,32 @@ curl http://localhost:5000/api
---
## Preispitivanje upravljanja stanjem
## Ponovno razmotrite upravljanje stanjem
U [prethodnoj lekciji](../3-data/README.md) uveli smo osnovni koncept stanja u našoj aplikaciji s globalnom varijablom `account` koja sadrži bankovne podatke za trenutno prijavljenog korisnika. Međutim, naša trenutna implementacija ima neke nedostatke. Pokušajte osvježiti stranicu dok ste na nadzornoj ploči. Što se događa?
U [prethodnoj lekciji](../3-data/README.md), predstavili smo osnovni koncept stanja u našoj aplikaciji s globalnom varijablom `account` koja sadrži bankovne podatke za trenutno prijavljenog korisnika. Međutim, naša trenutna implementacija ima neke nedostatke. Pokušajte osvježiti stranicu dok ste na nadzornoj ploči. Što se događa?
Postoje tri problema s trenutnim kodom:
- Stanje nije zadržano, jer vas osvježavanje preglednika vraća na stranicu za prijavu.
- Postoji više funkcija koje mijenjaju stanje. Kako aplikacija raste, to može otežati praćenje promjena i lako je zaboraviti ažurirati neku od njih.
- Stanje se ne čisti, pa kada kliknete na *Odjava*, podaci o računu i dalje ostaju prisutni iako ste na stranici za prijavu.
- Stanje nije trajno, jer osvježavanje preglednika vraća vas na stranicu za prijavu.
- Postoji više funkcija koje mijenjaju stanje. Kako aplikacija raste, to može otežati praćenje promjena i lako je zaboraviti ažurirati jednu.
- Stanje se ne čisti, pa kada kliknete na *Odjava*, podaci o računu i dalje ostaju, iako ste na stranici za prijavu.
Mogli bismo ažurirati naš kod kako bismo riješili ove probleme jedan po jedan, ali to bi stvorilo više dupliciranja koda i učinilo aplikaciju složenijom i težom za održavanje. Ili bismo mogli zastati na nekoliko minuta i preispitati našu strategiju.
Mogli bismo ažurirati naš kod kako bismo se pozabavili ovim problemima jedan po jedan, ali to bi stvorilo više dupliciranja koda i učinilo aplikaciju složenijom i težom za održavanje. Ili bismo mogli zastati na nekoliko minuta i ponovno razmisliti o našoj strategiji.
> Koje probleme zapravo pokušavamo riješiti?
[Upravljanje stanjem](https://en.wikipedia.org/wiki/State_management) odnosi se na pronalaženje dobrog pristupa za rješavanje ova dva specifična problema:
[Upravljanje stanjem](https://en.wikipedia.org/wiki/State_management) odnosi se na pronalaženje dobrog pristupa za rješavanje ovih dvaju specifičnih problema:
- Kako održati tokove podataka u aplikaciji razumljivima?
- Kako učiniti tokove podataka u aplikaciji razumljivima?
- Kako osigurati da su podaci o stanju uvijek sinkronizirani s korisničkim sučeljem (i obrnuto)?
Kada se pobrinete za ovo, bilo koji drugi problemi koje biste mogli imati možda će već biti riješeni ili će ih biti lakše riješiti. Postoji mnogo mogućih pristupa za rješavanje ovih problema, ali mi ćemo se odlučiti za uobičajeno rješenje koje se sastoji od **centralizacije podataka i načina na koje ih mijenjamo**. Tokovi podataka izgledali bi ovako:
Kada se pozabavite ovim pitanjima, svi drugi problemi koje biste mogli imati možda su već riješeni ili su postali lakši za rješavanje. Postoji mnogo mogućih pristupa za rješavanje ovih problema, ali mi ćemo se odlučiti za uobičajeno rješenje koje se sastoji od **centralizacije podataka i načina njihovog mijenjanja**. Tokovi podataka izgledali bi ovako:

> Ovdje nećemo pokriti dio gdje podaci automatski pokreću ažuriranje prikaza, jer je to povezano s naprednijim konceptima [reaktivnog programiranja](https://en.wikipedia.org/wiki/Reactive_programming). To je dobra tema za daljnje istraživanje ako želite dublje zaroniti.
> Ovdje nećemo pokriti dio gdje podaci automatski pokreću ažuriranje prikaza, jer je to povezano s naprednijim konceptima [reaktivnog programiranja](https://en.wikipedia.org/wiki/Reactive_programming). To je dobra tema za daljnje istraživanje ako ste spremni za dublje proučavanje.
✅ Postoji mnogo biblioteka s različitim pristupima upravljanju stanjem, a [Redux](https://redux.js.org) je popularna opcija. Pogledajte koncepte i obrasce koji se koriste jer je to često dobar način za učenje o potencijalnim problemima s kojima se možete suočiti u velikim web aplikacijama i kako ih riješiti.
✅ Postoji mnogo biblioteka s različitim pristupima upravljanju stanjem, a [Redux](https://redux.js.org) je popularna opcija. Pogledajte koncepte i obrasce koji se koriste jer je to često dobar način za učenje o potencijalnim problemima s kojima se možete suočiti u velikim web aplikacijama i kako ih možete riješiti.
### Zadatak
@ -75,11 +75,11 @@ let state = {
};
```
Ideja je *centralizirati* sve podatke naše aplikacije u jedan objekt stanja. Trenutno imamo samo `account` u stanju, tako da se puno toga ne mijenja, ali to stvara put za buduće nadogradnje.
Ideja je *centralizirati* sve podatke naše aplikacije u jednom objektu stanja. Trenutno imamo samo `account` u stanju, pa se puno toga ne mijenja, ali to stvara put za buduće promjene.
Također moramo ažurirati funkcije koje ga koriste. U funkcijama `register()` i `login()` zamijenite `account = ...` s `state.account = ...`;
Na vrhu funkcije `updateDashboard()` dodajte ovu liniju:
Na vrhu funkcije `updateDashboard()` dodajte ovaj redak:
```js
const account = state.account;
@ -89,9 +89,9 @@ Ovo refaktoriranje samo po sebi nije donijelo mnogo poboljšanja, ali ideja je b
## Praćenje promjena podataka
Sada kada smo postavili objekt `state` za pohranu naših podataka, sljedeći korak je centralizirati ažuriranja. Cilj je olakšati praćenje bilo kakvih promjena i kada se one događaju.
Sada kada smo postavili objekt `state` za pohranu naših podataka, sljedeći korak je centralizacija ažuriranja. Cilj je olakšati praćenje svih promjena i kada se one događaju.
Kako bismo izbjegli promjene na objektu `state`, također je dobra praksa smatrati ga [*nepromjenjivim*](https://en.wikipedia.org/wiki/Immutable_object), što znači da ga uopće nije moguće mijenjati. To također znači da morate stvoriti novi objekt stanja ako želite promijeniti bilo što u njemu. Na taj način gradite zaštitu od potencijalno neželjenih [nuspojava](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) i otvarate mogućnosti za nove značajke u svojoj aplikaciji, poput implementacije poništavanja/ponovnog izvršavanja, dok također olakšavate otklanjanje pogrešaka. Na primjer, mogli biste zabilježiti svaku promjenu napravljenu na stanju i zadržati povijest promjena kako biste razumjeli izvor pogreške.
Kako bismo izbjegli promjene na objektu `state`, također je dobra praksa smatrati ga [*nepromjenjivim*](https://en.wikipedia.org/wiki/Immutable_object), što znači da se uopće ne može mijenjati. To također znači da morate stvoriti novi objekt stanja ako želite nešto promijeniti u njemu. Na taj način gradite zaštitu od potencijalno neželjenih [nuspojava](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), i otvarate mogućnosti za nove značajke u svojoj aplikaciji, poput implementacije poništavanja/ponovnog izvršavanja, dok također olakšavate otklanjanje pogrešaka. Na primjer, mogli biste zabilježiti svaku promjenu napravljenu na stanju i zadržati povijest promjena kako biste razumjeli izvor greške.
U JavaScriptu možete koristiti [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) za stvaranje nepromjenjive verzije objekta. Ako pokušate napraviti promjene na nepromjenjivom objektu, bit će podignuta iznimka.
@ -99,7 +99,7 @@ U JavaScriptu možete koristiti [`Object.freeze()`](https://developer.mozilla.or
### Zadatak
Kreirajmo novu funkciju `updateState()`:
Stvorimo novu funkciju `updateState()`:
```js
function updateState(property, newData) {
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
U ovoj funkciji stvaramo novi objekt stanja i kopiramo podatke iz prethodnog stanja koristeći [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Zatim prepisujemo određeno svojstvo objekta stanja s novim podacima koristeći [notaciju zagrada](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` za dodjelu. Na kraju, zaključavamo objekt kako bismo spriječili izmjene koristeći `Object.freeze()`. Trenutno imamo samo svojstvo `account` pohranjeno u stanju, ali s ovim pristupom možete dodati koliko god svojstava trebate u stanje.
U ovoj funkciji stvaramo novi objekt stanja i kopiramo podatke iz prethodnog stanja koristeći [*operator širenja (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Zatim nadjačavamo određeno svojstvo objekta stanja novim podacima koristeći [notaciju zagrada](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` za dodjelu. Na kraju zaključavamo objekt kako bismo spriječili izmjene koristeći `Object.freeze()`. Trenutno imamo samo svojstvo `account` pohranjeno u stanju, ali s ovim pristupom možete dodati koliko god svojstava trebate u stanje.
Također ćemo ažurirati inicijalizaciju `state` kako bismo osigurali da je početno stanje također zamrznuto:
Također ćemo ažurirati inicijalizaciju `state` kako bismo osigurali da je početno stanje također zaključano:
```js
let state = Object.freeze({
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
Nakon toga, ažurirajte funkciju `register` zamjenom `state.account = result;` s:
Nakon toga, ažurirajte funkciju `register` zamjenom dodjele `state.account = result;` s:
```js
updateState('account', result);
@ -132,9 +132,9 @@ Isto učinite s funkcijom `login`, zamjenjujući `state.account = data;` s:
updateState('account', data);
```
Sada ćemo iskoristiti priliku da riješimo problem s podacima o računu koji se ne brišu kada korisnik klikne na *Odjava*.
Sada ćemo iskoristiti priliku da riješimo problem nečišćenja podataka o računu kada korisnik klikne na *Odjava*.
Kreirajte novu funkciju `logout()`:
Stvorite novu funkciju `logout()`:
```js
function logout() {
@ -143,49 +143,49 @@ function logout() {
}
```
U `updateDashboard()` zamijenite preusmjeravanje `return navigate('/login');` s `return logout();`;
U `updateDashboard()` zamijenite preusmjeravanje `return navigate('/login');` s `return logout()`;
Pokušajte registrirati novi račun, odjaviti se i ponovno prijaviti kako biste provjerili radi li sve ispravno.
> Savjet: možete pogledati sve promjene stanja dodavanjem `console.log(state)` na dno `updateState()` i otvaranjem konzole u alatima za razvoj preglednika.
## Zadržavanje stanja
## Trajno pohranjivanje stanja
Većina web aplikacija treba zadržati podatke kako bi ispravno radila. Svi kritični podaci obično se pohranjuju u bazu podataka i pristupa im se putem API-ja poslužitelja, poput podataka o korisničkom računu u našem slučaju. No, ponekad je također zanimljivo zadržati neke podatke na klijentskoj aplikaciji koja se pokreće u vašem pregledniku, za bolje korisničko iskustvo ili za poboljšanje performansi učitavanja.
Većina web aplikacija treba trajno pohranjivati podatke kako bi ispravno radila. Svi ključni podaci obično se pohranjuju u bazu podataka i pristupaju putem API-ja poslužitelja, poput podataka o korisničkom računu u našem slučaju. No, ponekad je također zanimljivo trajno pohraniti neke podatke na klijentskoj aplikaciji koja se pokreće u vašem pregledniku, za bolje korisničko iskustvo ili za poboljšanje performansi učitavanja.
Kada želite zadržati podatke u svom pregledniku, postoji nekoliko važnih pitanja koja biste trebali postaviti:
Kada želite trajno pohraniti podatke u pregledniku, postoji nekoliko važnih pitanja koja biste trebali postaviti:
- *Jesu li podaci osjetljivi?* Trebali biste izbjegavati pohranjivanje bilo kakvih osjetljivih podataka na klijentu, poput korisničkih lozinki.
- *Koliko dugo trebate zadržati te podatke?* Planirate li pristupiti tim podacima samo za trenutnu sesiju ili želite da budu pohranjeni zauvijek?
- *Koliko dugo trebate zadržati ove podatke?* Planirate li pristupiti ovim podacima samo za trenutnu sesiju ili želite da budu pohranjeni zauvijek?
Postoji više načina za pohranjivanje informacija unutar web aplikacije, ovisno o tome što želite postići. Na primjer, možete koristiti URL-ove za pohranjivanje upita za pretraživanje i učiniti ih dijeljivima između korisnika. Također možete koristiti [HTTP kolačiće](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ako se podaci trebaju dijeliti s poslužiteljem, poput informacija za [autentifikaciju](https://en.wikipedia.org/wiki/Authentication).
Postoji više načina za pohranjivanje informacija unutar web aplikacije, ovisno o tome što želite postići. Na primjer, možete koristiti URL-ove za pohranjivanje upita za pretraživanje i učiniti ih dijeljivima između korisnika. Također možete koristiti [HTTP kolačiće](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ako podaci trebaju biti dijeljeni s poslužiteljem, poput informacija o [autentifikaciji](https://en.wikipedia.org/wiki/Authentication).
Druga opcija je korištenje jedne od mnogih API-ja preglednika za pohranjivanje podataka. Dvije od njih su posebno zanimljive:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value pohrana](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) koja omogućuje zadržavanje podataka specifičnih za trenutnu web stranicu tijekom različitih sesija. Podaci spremljeni u njoj nikada ne istječu.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value pohrana](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) koja omogućuje trajno pohranjivanje podataka specifičnih za trenutnu web stranicu tijekom različitih sesija. Podaci pohranjeni u njoj nikada ne istječu.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): radi isto kao `localStorage`, osim što se podaci pohranjeni u njoj brišu kada sesija završi (kada se preglednik zatvori).
Napomena: oba ova API-ja omogućuju pohranjivanje samo [nizova](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ako želite pohraniti složene objekte, morat ćete ih serijalizirati u [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format koristeći [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Napomena da oba ova API-ja omogućuju pohranjivanje samo [nizova](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ako želite pohraniti složene objekte, morat ćete ih serijalizirati u [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format koristeći [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Ako želite stvoriti web aplikaciju koja ne radi s poslužiteljem, također je moguće stvoriti bazu podataka na klijentu koristeći [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ovo je rezervirano za napredne slučajeve upotrebe ili ako trebate pohraniti značajnu količinu podataka, jer je složenije za korištenje.
### Zadatak
Želimo da naši korisnici ostanu prijavljeni dok izričito ne kliknu na gumb *Odjava*, pa ćemo koristiti `localStorage` za pohranjivanje podataka o računu. Prvo, definirajmo ključ koji ćemo koristiti za pohranjivanje naših podataka.
Želimo da naši korisnici ostanu prijavljeni dok eksplicitno ne kliknu na gumb *Odjava*, pa ćemo koristiti `localStorage` za pohranjivanje podataka o računu. Prvo, definirajmo ključ koji ćemo koristiti za pohranjivanje naših podataka.
```js
const storageKey = 'savedAccount';
```
Zatim dodajte ovu liniju na kraj funkcije `updateState()`:
Zatim dodajte ovaj redak na kraj funkcije `updateState()`:
S ovim će se podaci o korisničkom računu zadržati i uvijek biti ažurirani jer smo prethodno centralizirali sva ažuriranja stanja. Ovo je trenutak kada počinjemo imati koristi od svih naših prethodnih refaktora 🙂.
S ovim, podaci o korisničkom računu bit će trajno pohranjeni i uvijek ažurirani jer smo prethodno centralizirali sva ažuriranja stanja. Ovo je trenutak kada počinjemo uživati u svim našim prethodnim refaktoriranjima 🙂.
Budući da su podaci spremljeni, također se moramo pobrinuti za njihovo vraćanje kada se aplikacija učita. Budući da ćemo početi imati više koda za inicijalizaciju, možda bi bilo dobro stvoriti novu funkciju `init`, koja također uključuje naš prethodni kod na dnu `app.js`:
Kako su podaci pohranjeni, također moramo voditi računa o njihovom vraćanju kada se aplikacija učita. Budući da ćemo početi imati više inicijalizacijskog koda, možda bi bilo dobro stvoriti novu funkciju `init`, koja također uključuje naš prethodni kod na dnu `app.js`:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Ovdje dohvaćamo spremljene podatke, a ako ih ima, ažuriramo stanje u skladu s tim. Važno je to učiniti *prije* ažuriranja rute, jer bi moglo postojati koda koji se oslanja na stanje tijekom ažuriranja stranice.
Ovdje dohvaćamo pohranjene podatke, i ako ih ima, ažuriramo stanje u skladu s tim. Važno je to učiniti *prije* ažuriranja rute, jer može postojati kod koji se oslanja na stanje tijekom ažuriranja stranice.
Također možemo učiniti stranicu *Nadzorna ploča* zadanom stranicom naše aplikacije, jer sada zadržavamo podatke o računu. Ako se ne pronađu podaci, nadzorna ploča ionako preusmjerava na stranicu *Prijava*. U `updateRoute()` zamijenite zadano`return navigate('/login');` s `return navigate('/dashboard');`.
Također možemo učiniti stranicu *Nadzorna ploča* zadanom stranicom naše aplikacije, jer sada trajno pohranjujemo podatke o računu. Ako se ne pronađu podaci, nadzorna ploča se brine o preusmjeravanju na stranicu *Prijava*. U `updateRoute()` zamijenite rezervnu opciju`return navigate('/login');` s `return navigate('/dashboard');`.
Sada se prijavite u aplikaciju i pokušajte osvježiti stranicu. Trebali biste ostati na nadzornoj ploči. S tim ažuriranjem riješili smo sve naše početne probleme...
## Osvježavanje podataka
## Ažuriranje podataka
...Ali možda smo također stvorili novi problem. Ups!
Idite na nadzornu ploču koristeći račun `test`, a zatim pokrenite ovu naredbu u terminalu kako biste stvorili novu transakciju:
Idite na nadzornu ploču koristeći račun `test`, zatim pokrenite ovu naredbu u terminalu kako biste stvorili novu transakciju:
Pokušajte sada osvježiti stranicu nadzorne ploče u pregledniku. Što se događa? Vidite li novu transakciju?
Pokušajte osvježiti stranicu nadzorne ploče u pregledniku sada. Što se događa? Vidite li novu transakciju?
Stanje se zadržava neograničeno zahvaljujući `localStorage`, ali to također znači da se nikada ne ažurira dok se ne odjavite iz aplikacije i ponovno prijavite!
Stanje je trajno pohranjeno zahvaljujući `localStorage`, ali to također znači da se nikada ne ažurira dok se ne odjavite iz aplikacije i ponovno prijavite!
Jedna moguća strategija za rješavanje ovog problema je ponovno učitavanje podataka o računu svaki put kada se učita nadzorna ploča, kako bi se izbjegli zastarjeli podaci.
Jedna moguća strategija za rješavanje toga je ponovno učitavanje podataka o računu svaki put kada se učita nadzorna ploča, kako bi se izbjegli zastarjeli podaci.
### Zadatak
Kreirajte novu funkciju `updateAccountData`:
Stvorite novu funkciju `updateAccountData`:
```js
async function updateAccountData() {
@ -249,7 +249,7 @@ async function updateAccountData() {
Ova metoda provjerava jesmo li trenutno prijavljeni, a zatim ponovno učitava podatke o računu s poslužitelja.
Kreirajte drugu funkciju pod nazivom`refresh`:
Stvorite drugu funkciju nazvanu`refresh`:
```js
async function refresh() {
@ -267,21 +267,21 @@ const routes = {
};
```
Pokušajte sada ponovno učitati nadzornu ploču, trebala bi prikazati ažurirane podatke o računu.
Pokušajte ponovno učitati nadzornu ploču sada, trebala bi prikazati ažurirane podatke o računu.
---
## 🚀 Izazov
Sada kada ponovno učitavamo podatke o računu svaki put kada se učita nadzorna ploča, mislite li da još uvijek trebamo zadržavati *sve podatke o računu*?
Sada kada ponovno učitavamo podatke o računu svaki put kada se učita nadzorna ploča, mislite li da još uvijek trebamo trajno pohranjivati *sve podatke o računu*?
Pokušajte zajedno promijeniti što se sprema i učitava iz `localStorage` tako da uključuje samo ono što je apsolutno potrebno za rad aplikacije.
Pokušajte zajedno promijeniti ono što se pohranjuje i učitava iz `localStorage` tako da uključuje samo ono što je apsolutno potrebno za rad aplikacije.
## Kviz nakon predavanja
[Kviz nakon predavanja](https://ff-quizzes.netlify.app/web/quiz/48)
@ -291,4 +291,4 @@ Evo primjera rezultata nakon dovršetka zadatka:
---
**Odricanje od odgovornosti**:
Ovaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja proizlaze iz korištenja ovog prijevoda.
Ovaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane čovjeka. Ne preuzimamo odgovornost za nesporazume ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.
# Banki alkalmazás építése 4. rész: Állapotkezelés fogalmai
# Banki alkalmazás készítése 4. rész: Az állapotkezelés alapjai
## Előadás előtti kvíz
@ -15,13 +15,13 @@ CO_OP_TRANSLATOR_METADATA:
### Bevezetés
Ahogy egy webalkalmazás növekszik, egyre nehezebb nyomon követni az összes adatáramlást. Melyik kód kapja meg az adatokat, melyik oldal használja fel, hol és mikor kell frissíteni... könnyen előfordulhat, hogy rendezetlen kódot kapunk, amelyet nehéz karbantartani. Ez különösen igaz, ha az adatokat meg kell osztani az alkalmazás különböző oldalai között, például a felhasználói adatokat. Az *állapotkezelés* fogalma mindig is létezett mindenféle programban, de ahogy a webalkalmazások egyre bonyolultabbá válnak, ez ma már kulcsfontosságú szempont a fejlesztés során.
Ahogy egy webalkalmazás növekszik, egyre nehezebb nyomon követni az összes adatáramlást. Melyik kód kapja meg az adatokat, melyik oldal használja fel, hol és mikor kell frissíteni... könnyen előfordulhat, hogy a kód rendezetlenné válik, és nehéz karbantartani. Ez különösen igaz, ha az adatokat meg kell osztani az alkalmazás különböző oldalai között, például a felhasználói adatokat. Az *állapotkezelés* fogalma mindig is létezett mindenféle programban, de ahogy a webalkalmazások egyre összetettebbé válnak, ez ma már kulcsfontosságú szempont a fejlesztés során.
Ebben az utolsó részben átnézzük az általunk épített alkalmazást, hogy újragondoljuk az állapotkezelést, lehetővé téve a böngésző frissítését bármikor, és az adatok megőrzését a felhasználói munkamenetek között.
Ebben az utolsó részben áttekintjük az eddig elkészített alkalmazást, hogy újragondoljuk az állapotkezelését, lehetővé téve a böngésző frissítését bármely ponton, és az adatok megőrzését a felhasználói munkamenetek között.
### Előfeltétel
A webalkalmazás [adatlekérés](../3-data/README.md) részét be kell fejezned ehhez a leckéhez. Továbbá telepítened kell a [Node.js](https://nodejs.org) programot, és [helyileg futtatnod kell a szerver API-t](../api/README.md), hogy kezelni tudd a fiókadatokat.
A [adatlekérés](../3-data/README.md) részét már el kell végezned az alkalmazásnak ehhez a leckéhez. Továbbá telepítened kell a [Node.js](https://nodejs.org) programot, és [helyileg futtatnod kell a szerver API-t](../api/README.md), hogy kezelni tudd a fiókadatokat.
Ellenőrizheted, hogy a szerver megfelelően fut-e, ha végrehajtod ezt a parancsot egy terminálban:
@ -32,32 +32,32 @@ curl http://localhost:5000/api
---
## Állapotkezelés újragondolása
## Az állapotkezelés újragondolása
Az [előző leckében](../3-data/README.md) bevezettük az állapot alapfogalmát az alkalmazásunkban a globális `account` változóval, amely tartalmazza az aktuálisan bejelentkezett felhasználó banki adatait. Azonban a jelenlegi megvalósításunknak vannak hiányosságai. Próbáld meg frissíteni az oldalt, amikor a vezérlőpulton vagy. Mi történik?
Az [előző leckében](../3-data/README.md) bevezettük az állapot alapfogalmát az alkalmazásunkban a globális `account` változóval, amely tartalmazza az aktuálisan bejelentkezett felhasználó banki adatait. Azonban a jelenlegi megvalósításunknak vannak hibái. Próbáld meg frissíteni az oldalt, amikor a műszerfalon vagy. Mi történik?
A jelenlegi kóddal három probléma van:
- Az állapot nem marad meg, mivel a böngésző frissítése visszavisz a bejelentkezési oldalra.
- Több funkció módosítja az állapotot. Ahogy az alkalmazás növekszik, nehéz lehet nyomon követni a változásokat, és könnyen elfelejthetünk frissíteni egyet.
- Több funkció módosítja az állapotot. Ahogy az alkalmazás növekszik, nehéz lehet nyomon követni a változásokat, és könnyű elfelejteni egy frissítést.
- Az állapot nincs megtisztítva, így amikor a *Kijelentkezés* gombra kattintasz, a fiókadatok még mindig ott vannak, annak ellenére, hogy a bejelentkezési oldalon vagy.
Frissíthetnénk a kódunkat, hogy egyenként kezeljük ezeket a problémákat, de ez több kódismétlést eredményezne, és az alkalmazás bonyolultabbá és nehezebben karbantarthatóvá válna. Vagy megállhatnánk néhány percre, és újragondolhatnánk a stratégiánkat.
> Milyen problémákat próbálunk valójában megoldani?
Az [állapotkezelés](https://en.wikipedia.org/wiki/State_management) lényege, hogy jó megközelítést találjunk ennek a két konkrét problémának a megoldására:
Az [állapotkezelés](https://en.wikipedia.org/wiki/State_management) lényege, hogy jó megközelítést találjunk e két konkrét probléma megoldására:
- Hogyan lehet az adatáramlásokat egy alkalmazásban érthetővé tenni?
- Hogyan lehet az állapotadatokat mindig szinkronban tartani a felhasználói felülettel (és fordítva)?
Ha ezeket megoldottad, bármilyen más probléma, amivel szembesülhetsz, vagy már megoldódott, vagy könnyebben megoldhatóvá vált. Számos lehetséges megközelítés létezik ezeknek a problémáknak a megoldására, de mi egy gyakori megoldást választunk, amely **az adatok és a módosítási lehetőségek központosítását** foglalja magában. Az adatáramlás így nézne ki:
Ha ezeket megoldottad, bármely más probléma, amellyel szembesülhetsz, vagy már megoldódott, vagy könnyebben megoldhatóvá vált. Számos lehetséges megközelítés létezik ezeknek a problémáknak a megoldására, de mi egy gyakori megoldást választunk, amely abból áll, hogy **központosítjuk az adatokat és a módokat, ahogyan azokat megváltoztatjuk**. Az adatáramlások így néznének ki:


> Itt nem térünk ki arra a részre, ahol az adatok automatikusan frissítik a nézetet, mivel ez a [Reaktív programozás](https://en.wikipedia.org/wiki/Reactive_programming) fejlettebb fogalmaihoz kapcsolódik. Ez egy jó következő téma, ha mélyebben szeretnél belemerülni.
> Itt nem térünk ki arra a részre, ahol az adatok automatikusan frissítik a nézetet, mivel ez összefügg a [Reaktív programozás](https://en.wikipedia.org/wiki/Reactive_programming) fejlettebb fogalmaival. Ez egy jó következő téma, ha mélyebben szeretnél elmerülni.
✅ Számos könyvtár létezik különböző megközelítésekkel az állapotkezeléshez, például a [Redux](https://redux.js.org), amely egy népszerű opció. Nézd meg a használt fogalmakat és mintákat, mivel gyakran jó módja annak, hogy megtudd, milyen potenciális problémákkal szembesülhetsz nagy webalkalmazásokban, és hogyan lehet ezeket megoldani.
✅ Számos könyvtár létezik különböző megközelítésekkel az állapotkezelésre, például a [Redux](https://redux.js.org), amely egy népszerű opció. Nézd meg a használt fogalmakat és mintákat, mivel gyakran jó módja annak, hogy megtudd, milyen potenciális problémákkal szembesülhetsz nagy webalkalmazásokban, és hogyan lehet ezeket megoldani.
### Feladat
@ -67,7 +67,7 @@ Kezdjünk egy kis refaktorálással. Cseréld le az `account` deklarációt:
let account = null;
```
Erre:
Ezzel:
```js
let state = {
@ -77,7 +77,7 @@ let state = {
Az ötlet az, hogy *központosítsuk* az összes alkalmazásadatot egyetlen állapotobjektumban. Jelenleg csak az `account` van az állapotban, így ez nem változtat sokat, de megnyitja az utat a további fejlesztések előtt.
Frissítenünk kell az ezt használó funkciókat is. A `register()` és `login()` funkciókban cseréld le az `account = ...` kifejezést `state.account = ...`-ra;
Frissítenünk kell az azt használó funkciókat is. A `register()` és `login()` funkciókban cseréld le az `account = ...` kifejezést `state.account = ...`-ra.
Az `updateDashboard()` funkció elején add hozzá ezt a sort:
@ -87,13 +87,13 @@ const account = state.account;
Ez a refaktorálás önmagában nem hozott sok javulást, de az ötlet az volt, hogy lefektessük az alapokat a következő változtatásokhoz.
## Adatváltozások nyomon követése
## Az adatváltozások nyomon követése
Most, hogy létrehoztuk az`state` objektumot az adataink tárolására, a következő lépés az, hogy központosítsuk a frissítéseket. A cél az, hogy könnyebb legyen nyomon követni a változásokat és azt, hogy mikor történnek.
Most, hogy létrehoztuk a `state` objektumot az adataink tárolására, a következő lépés az, hogy központosítsuk a frissítéseket. A cél az, hogy könnyebb legyen nyomon követni a változásokat és azt, hogy mikor történnek.
Az `state` objektum módosításának elkerülése érdekében jó gyakorlatnak számít, ha azt [*változtathatatlannak*](https://en.wikipedia.org/wiki/Immutable_object) tekintjük, ami azt jelenti, hogy egyáltalán nem lehet módosítani. Ez azt is jelenti, hogy új állapotobjektumot kell létrehoznod, ha bármit meg akarsz változtatni benne. Ezzel védelmet építesz ki a potenciálisan nem kívánt [mellékhatások](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) ellen, és lehetőséget nyitsz új funkciók bevezetésére az alkalmazásodban, például visszavonás/újra végrehajtás implementálására, miközben megkönnyíted a hibakeresést. Például naplózhatnád az állapotban végrehajtott minden változást, és megőrizhetnéd a változások történetét, hogy megértsd egy hiba forrását.
Az is jó gyakorlat, ha az állapotobjektumot [*változtathatatlannak*](https://en.wikipedia.org/wiki/Immutable_object) tekintjük, ami azt jelenti, hogy egyáltalán nem módosítható. Ez azt is jelenti, hogy új állapotobjektumot kell létrehoznod, ha bármit meg akarsz változtatni benne. Ezzel védelmet építesz ki a potenciálisan nem kívánt [mellékhatások](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) ellen, és lehetőséget nyitsz új funkciók bevezetésére az alkalmazásodban, például az undo/redo megvalósítására, miközben megkönnyíted a hibakeresést. Például naplózhatnád az állapoton végrehajtott minden változást, és nyomon követhetnéd a változások történetét, hogy megértsd egy hiba forrását.
JavaScriptben az [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) segítségével hozhatsz létre egy változtathatatlan objektumot. Ha megpróbálsz változtatásokat végrehajtani egy változtathatatlan objektumon, kivétel keletkezik.
JavaScriptben az [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) segítségével hozhatsz létre egy változtathatatlan verziót egy objektumból. Ha megpróbálsz változtatásokat végrehajtani egy változtathatatlan objektumon, kivétel keletkezik.
✅ Tudod, mi a különbség a *sekély* és a *mély* változtathatatlan objektum között? Olvass róla [itt](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
Ebben a funkcióban létrehozunk egy új állapotobjektumot, és az előző állapotból másoljuk az adatokat a [*spread (`...`) operátor*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) segítségével. Ezután felülírunk egy adott tulajdonságot az állapotobjektumban az új adatokkal a [zárójel notáció](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` használatával. Végül zároljuk az objektumot, hogy megakadályozzuk a módosításokat az `Object.freeze()` segítségével. Jelenleg csak az `account` tulajdonságot tároljuk az állapotban, de ezzel a megközelítéssel annyi tulajdonságot adhatsz hozzá az állapothoz, amennyire szükséged van.
Ebben a funkcióban létrehozunk egy új állapotobjektumot, és az előző állapotból másolunk adatokat a [*spread (`...`) operátor*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) segítségével. Ezután felülírunk egy adott tulajdonságot az állapotobjektumban az új adatokkal a [szögletes zárójel](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` használatával. Végül zároljuk az objektumot, hogy megakadályozzuk a módosításokat az `Object.freeze()` segítségével. Jelenleg csak az `account` tulajdonságot tároljuk az állapotban, de ezzel a megközelítéssel annyi tulajdonságot adhatsz hozzá az állapothoz, amennyire szükséged van.
Frissítsük az `state` inicializálását is, hogy az inicializálási állapot is zárolva legyen:
@ -120,13 +120,13 @@ let state = Object.freeze({
});
```
Ezután frissítsük a `register` funkciót az`state.account = result;` kifejezés lecserélésével:
Ezután frissítsük a `register` funkciót azzal, hogy az `state.account = result;` hozzárendelést lecseréljük erre:
```js
updateState('account', result);
```
Ugyanezt tegyük a `login` funkcióval, cseréljük le az `state.account = data;` kifejezést:
Ugyanezt tegyük a `login` funkcióval, cseréljük le az `state.account = data;` kifejezést erre:
```js
updateState('account', data);
@ -143,31 +143,31 @@ function logout() {
}
```
Az `updateDashboard()`-ban cseréld le az `return navigate('/login');` átirányítást `return logout();`-ra;
Az `updateDashboard()`-ban cseréld le az `return navigate('/login');` átirányítást erre: `return logout();`
Próbálj meg regisztrálni egy új fiókot, kijelentkezni, majd újra bejelentkezni, hogy ellenőrizd, minden továbbra is megfelelően működik-e.
> Tipp: megnézheted az összes állapotváltozást, ha hozzáadod a `console.log(state)` kifejezést az `updateState()` aljára, és megnyitod a böngésződ fejlesztői eszközeinek konzolját.
> Tipp: az összes állapotváltozást megtekintheted, ha hozzáadod a `console.log(state)` kifejezést az `updateState()` aljára, és megnyitod a böngésződ fejlesztői eszközeinek konzolját.
## Az állapot megőrzése
A legtöbb webalkalmazásnak meg kell őriznie az adatokat, hogy megfelelően működjön. Az összes kritikus adatot általában egy adatbázisban tárolják, és egy szerver API-n keresztül érik el, például a felhasználói fiókadatokat esetünkben. De néha az is érdekes lehet, hogy az adatokat megőrizzük a böngészőben futó kliensalkalmazásban, a jobb felhasználói élmény vagy a betöltési teljesítmény javítása érdekében.
A legtöbb webalkalmazásnak szüksége van az adatok megőrzésére, hogy megfelelően működjön. Az összes kritikus adatot általában egy adatbázisban tárolják, és egy szerver API-n keresztül érik el, például a felhasználói fiókadatokat esetünkben. De néha az is érdekes lehet, hogy néhány adatot megőrizzünk a böngészőben futó kliensalkalmazásban, jobb felhasználói élmény vagy a betöltési teljesítmény javítása érdekében.
Amikor adatokat szeretnél megőrizni a böngésződben, néhány fontos kérdést fel kell tenned magadnak:
- *Érzékeny az adat?* Kerüld az érzékeny adatok, például a felhasználói jelszavak tárolását a kliensen.
- *Mennyi ideig szeretnéd megőrizni ezeket az adatokat?* Csak az aktuális munkamenet során szeretnéd elérni ezeket az adatokat, vagy örökre tárolni szeretnéd őket?
- *Mennyi ideig szeretnéd megőrizni ezeket az adatokat?* Csak az aktuális munkamenethez szeretnéd hozzáférni ezekhez az adatokhoz, vagy örökre tárolni szeretnéd őket?
Számos módja van az információk tárolásának egy webalkalmazásban, attól függően, hogy mit szeretnél elérni. Például használhatod az URL-eket egy keresési lekérdezés tárolására, és megoszthatod azt más felhasználókkal. Használhatsz [HTTP sütiket](https://developer.mozilla.org/docs/Web/HTTP/Cookies), ha az adatokat meg kell osztani a szerverrel, például az [azonosítási](https://en.wikipedia.org/wiki/Authentication) információkat.
Számos módja van az információk tárolásának egy webalkalmazásban, attól függően, hogy mit szeretnél elérni. Például használhatod az URL-eket egy keresési lekérdezés tárolására, és megoszthatod azt a felhasználók között. Használhatsz [HTTP sütiket](https://developer.mozilla.org/docs/Web/HTTP/Cookies), ha az adatokat meg kell osztani a szerverrel, például az [azonosítási](https://en.wikipedia.org/wiki/Authentication) információkat.
Egy másik lehetőség, hogy a böngésző API-k egyikét használod az adatok tárolására. Két különösen érdekes API létezik:
Egy másik lehetőség, hogy a böngésző API-k egyikét használod az adatok tárolására. Kettő különösen érdekes:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): egy [kulcs/érték tároló](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), amely lehetővé teszi az adott webhelyhez tartozó adatok megőrzését különböző munkamenetek között. Az itt tárolt adatok soha nem járnak le.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): egy [kulcs/érték tároló](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), amely lehetővé teszi az adott webhelyhez kapcsolódó adatok megőrzését különböző munkamenetek között. Az itt tárolt adatok soha nem járnak le.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ez ugyanúgy működik, mint a `localStorage`, kivéve, hogy az itt tárolt adatok törlődnek, amikor a munkamenet véget ér (amikor a böngészőt bezárják).
Fontos megjegyezni, hogy mindkét API csak [sztringeket](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) tud tárolni. Ha összetett objektumokat szeretnél tárolni, akkor azokat a [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) formátumba kell sorosítanod a [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) segítségével.
Fontos megjegyezni, hogy mindkét API csak [szövegeket](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) tud tárolni. Ha összetett objektumokat szeretnél tárolni, akkor azokat a [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) formátumba kell sorosítanod a [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) segítségével.
✅ Ha olyan webalkalmazást szeretnél létrehozni, amely nem működik szerverrel, akkor az [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) segítségével létrehozhatsz egy adatbázist a kliensen. Ez az opció fejlettebb esetekre vagy jelentős mennyiségű adat tárolására van fenntartva, mivel bonyolultabb a használata.
✅ Ha olyan webalkalmazást szeretnél létrehozni, amely nem működik szerverrel, akkor az ügyféloldalon is létrehozhatsz adatbázist az [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) segítségével. Ez az opció haladó felhasználási esetekre vagy jelentős mennyiségű adat tárolására van fenntartva, mivel bonyolultabb a használata.
### Feladat
@ -183,9 +183,9 @@ Ezután add hozzá ezt a sort az `updateState()` funkció végéhez:
Ezzel a felhasználói fiókadatok megmaradnak, és mindig naprakészek lesznek, mivel korábban központosítottuk az összes állapotfrissítést. Itt kezdjük el élvezni az összes korábbi refaktorálás előnyeit 🙂.
Ezzel a felhasználói fiókadatok megőrzésre kerülnek, és mindig naprakészek lesznek, mivel korábban központosítottuk az összes állapotfrissítést. Itt kezdjük el élvezni az összes korábbi refaktorálás előnyeit 🙂.
Mivel az adatok mentésre kerülnek, gondoskodnunk kell azok visszaállításáról is, amikor az alkalmazás betöltődik. Mivel egyre több inicializáló kódunk lesz, érdemes lehet létrehozni egy új `init` funkciót, amely tartalmazza a korábbi kódot is az `app.js` alján:
Mivel az adatok mentésre kerülnek, gondoskodnunk kell azok visszaállításáról is, amikor az alkalmazás betöltődik. Mivel egyre több inicializáló kódunk lesz, érdemes lehet létrehozni egy új `init` funkciót, amely magában foglalja a korábbi kódot is az `app.js` alján:
```js
function init() {
@ -204,15 +204,15 @@ init();
Itt visszanyerjük a mentett adatokat, és ha van ilyen, frissítjük az állapotot ennek megfelelően. Fontos, hogy ezt *azelőtt* tegyük meg, hogy frissítenénk az útvonalat, mivel lehet, hogy van olyan kód, amely az állapotra támaszkodik az oldal frissítése során.
Az alkalmazás alapértelmezett oldalának is megtehetjük a *Vezérlőpult* oldalt, mivel most már megőrizzük a fiókadatokat. Ha nem található adat, a vezérlőpult gondoskodik arról, hogy átirányítson a *Bejelentkezés* oldalra. Az `updateRoute()`-ban cseréld le az alapértelmezett `return navigate('/login');` kifejezést `return navigate('/dashboard');`-ra.
Az alkalmazás alapértelmezett oldalának is megtehetjük a *Műszerfal* oldalt, mivel most már megőrizzük a fiókadatokat. Ha nem található adat, a műszerfal gondoskodik arról, hogy átirányítson a *Bejelentkezés* oldalra. Az `updateRoute()`-ban cseréld le az alapértelmezett `return navigate('/login');` kifejezést erre: `return navigate('/dashboard');`.
Most jelentkezz be az alkalmazásba, és próbáld meg frissíteni az oldalt. A vezérlőpulton kell maradnod. Ezzel a frissítéssel gondoskodtunk az összes kezdeti problémánkról...
Most jelentkezz be az alkalmazásba, és próbáld meg frissíteni az oldalt. A műszerfalon kell maradnod. Ezzel a frissítéssel gondoskodtunk az összes kezdeti problémánkról...
## Az adatok frissítése
...De lehet, hogy egy új problémát is létrehoztunk. Hoppá!
Menj a vezérlőpultra a `test` fiókkal, majd futtasd ezt a parancsot egy terminálban, hogy létrehozz egy új tranzakciót:
Menj a műszerfalra a `test` fiókkal, majd futtasd ezt a parancsot egy terminálban, hogy létrehozz egy új tranzakciót:
Próbáld meg frissíteni a vezérlőpult oldalt a böngészőben. Mi történik? Látod az új tranzakciót?
Most próbáld meg frissíteni a műszerfal oldalát a böngészőben. Mi történik? Látod az új tranzakciót?
Az állapot határozatlan ideig megmarad a `localStorage`-nak köszönhetően, de ez azt is jelenti, hogy soha nem frissül, amíg ki nem jelentkezel az alkalmazásból, majd újra be
[Valósítsd meg a "Tranzakció hozzáadása" párbeszédablakot](assignment.md)
Az állapot határozatlan ideig megőrzésre kerül a `localStorage`-nak köszönhetően, de ez azt is jelenti, hogy soha nem frissül, amíg ki nem jelentkezel az alkalmazásból, majd újra be nem
[Előadás utáni kvíz](https://ff-quizzes.netlify.app/web/quiz/48)
Íme egy példakép az elkészült feladatról, amely bemutatja a "Tranzakció hozzáadása" párbeszédablakot:
## Feladat


---
**Felelősségkizárás**:
Ezt a dokumentumot az [Co-op Translator](https://github.com/Azure/co-op-translator) AI fordítószolgáltatás segítségével fordítottuk le. Bár törekszünk a pontosságra, kérjük, vegye figyelembe, hogy az automatikus fordítások hibákat vagy pontatlanságokat tartalmazhatnak. Az eredeti dokumentum az eredeti nyelvén tekintendő hiteles forrásnak. Kritikus információk esetén javasolt a professzionális, emberi fordítás igénybevétele. Nem vállalunk felelősséget a fordítás használatából eredő félreértésekért vagy téves értelmezésekért.
**Felelősségkizárása**:
Ez a dokumentum az [Co-op Translator](https://github.com/Azure/co-op-translator) AI fordítási szolgáltatás segítségével került lefordításra. Bár törekszünk a pontosságra, kérjük, vegye figyelembe, hogy az automatikus fordítások hibákat vagy pontatlanságokat tartalmazhatnak. Az eredeti dokumentum az eredeti nyelvén tekintendő hiteles forrásnak. Kritikus információk esetén javasolt professzionális emberi fordítást igénybe venni. Nem vállalunk felelősséget semmilyen félreértésért vagy téves értelmezésért, amely a fordítás használatából eredhet.
Seiring dengan berkembangnya aplikasi web, menjadi tantangan untuk melacak semua aliran data. Kode mana yang mendapatkan data, halaman mana yang menggunakannya, di mana dan kapan data perlu diperbarui...mudah sekali berakhir dengan kode yang berantakan dan sulit untuk dipelihara. Hal ini terutama berlaku ketika Anda perlu berbagi data di antara berbagai halaman aplikasi Anda, misalnya data pengguna. Konsep *manajemen state* selalu ada di semua jenis program, tetapi karena aplikasi web terus berkembang dalam kompleksitas, sekarang menjadi poin penting untuk dipikirkan selama pengembangan.
Seiring dengan berkembangnya aplikasi web, menjadi tantangan untuk melacak semua aliran data. Kode mana yang mendapatkan data, halaman mana yang menggunakannya, di mana dan kapan data perlu diperbarui...mudah sekali berakhir dengan kode yang berantakan dan sulit untuk dikelola. Hal ini terutama berlaku ketika Anda perlu berbagi data di antara berbagai halaman aplikasi Anda, misalnya data pengguna. Konsep *manajemen state* selalu ada di semua jenis program, tetapi karena aplikasi web terus berkembang dalam kompleksitas, sekarang menjadi poin penting untuk dipikirkan selama pengembangan.
Dalam bagian terakhir ini, kita akan meninjau kembali aplikasi yang telah kita bangun untuk memikirkan ulang bagaimana state dikelola, memungkinkan dukungan untuk penyegaran browser kapan saja, dan mempertahankan data di antara sesi pengguna.
@ -32,32 +32,32 @@ curl http://localhost:5000/api
---
## Memikirkan Ulang Manajemen State
## Memikirkan ulang manajemen state
Dalam [pelajaran sebelumnya](../3-data/README.md), kita memperkenalkan konsep dasar state dalam aplikasi kita dengan variabel global `account` yang berisi data bank untuk pengguna yang sedang masuk. Namun, implementasi kita saat ini memiliki beberapa kekurangan. Coba segarkan halaman saat Anda berada di dashboard. Apa yang terjadi?
Dalam [pelajaran sebelumnya](../3-data/README.md), kami memperkenalkan konsep dasar state dalam aplikasi kami dengan variabel global `account` yang berisi data bank untuk pengguna yang sedang masuk. Namun, implementasi kami saat ini memiliki beberapa kekurangan. Coba segarkan halaman saat Anda berada di dashboard. Apa yang terjadi?
Ada 3 masalah dengan kode saat ini:
- State tidak dipertahankan, karena penyegaran browser membawa Anda kembali ke halaman login.
- Ada beberapa fungsi yang memodifikasi state. Seiring dengan bertambahnya ukuran aplikasi, hal ini dapat membuat perubahan sulit dilacak dan mudah lupa untuk memperbarui salah satunya.
- Ada beberapa fungsi yang memodifikasi state. Seiring dengan berkembangnya aplikasi, hal ini dapat membuat perubahan sulit dilacak dan mudah lupa untuk memperbarui salah satunya.
- State tidak dibersihkan, sehingga ketika Anda mengklik *Logout*, data akun masih ada meskipun Anda berada di halaman login.
Kita bisa memperbarui kode kita untuk mengatasi masalah ini satu per satu, tetapi itu akan menciptakan lebih banyak duplikasi kode dan membuat aplikasi lebih kompleks serta sulit dipelihara. Atau kita bisa berhenti sejenak dan memikirkan ulang strategi kita.
Kami dapat memperbarui kode kami untuk mengatasi masalah ini satu per satu, tetapi itu akan menciptakan lebih banyak duplikasi kode dan membuat aplikasi lebih kompleks serta sulit untuk dikelola. Atau kami dapat berhenti sejenak dan memikirkan ulang strategi kami.
> Masalah apa yang sebenarnya kita coba selesaikan di sini?
[Manajemen state](https://en.wikipedia.org/wiki/State_management) adalah tentang menemukan pendekatan yang baik untuk menyelesaikan dua masalah utama ini:
[Manajemen state](https://en.wikipedia.org/wiki/State_management) adalah tentang menemukan pendekatan yang baik untuk menyelesaikan dua masalah khusus ini:
- Bagaimana cara menjaga aliran data dalam aplikasi tetap mudah dipahami?
- Bagaimana cara menjaga aliran data dalam aplikasi tetap dapat dipahami?
- Bagaimana cara menjaga data state selalu sinkron dengan antarmuka pengguna (dan sebaliknya)?
Setelah Anda menangani ini, masalah lain yang mungkin Anda miliki mungkin sudah teratasi atau menjadi lebih mudah untuk diperbaiki. Ada banyak pendekatan yang mungkin untuk menyelesaikan masalah ini, tetapi kita akan menggunakan solusi umum yang terdiri dari **sentralisasi data dan cara untuk mengubahnya**. Aliran data akan berjalan seperti ini:
Setelah Anda mengatasi masalah ini, masalah lain yang mungkin Anda miliki mungkin sudah teratasi atau menjadi lebih mudah untuk diperbaiki. Ada banyak pendekatan yang mungkin untuk menyelesaikan masalah ini, tetapi kami akan menggunakan solusi umum yang terdiri dari **memusatkan data dan cara untuk mengubahnya**. Aliran data akan berjalan seperti ini:

> Di sini kita tidak akan membahas bagian di mana data secara otomatis memicu pembaruan tampilan, karena itu terkait dengan konsep yang lebih maju dari [Pemrograman Reaktif](https://en.wikipedia.org/wiki/Reactive_programming). Ini adalah subjek lanjutan yang bagus jika Anda ingin mendalami lebih jauh.
> Di sini kami tidak akan membahas bagian di mana data secara otomatis memicu pembaruan tampilan, karena itu terkait dengan konsep yang lebih maju dari [Pemrograman Reaktif](https://en.wikipedia.org/wiki/Reactive_programming). Ini adalah subjek lanjutan yang bagus jika Anda ingin mendalami lebih jauh.
✅ Ada banyak pustaka di luar sana dengan pendekatan berbeda untuk manajemen state, [Redux](https://redux.js.org) menjadi salah satu opsi populer. Lihatlah konsep dan pola yang digunakan karena sering kali menjadi cara yang baik untuk mempelajari potensi masalah yang mungkin Anda hadapi dalam aplikasi web besar dan bagaimana cara menyelesaikannya.
✅ Ada banyak pustaka di luar sana dengan pendekatan berbeda untuk manajemen state, [Redux](https://redux.js.org) menjadi salah satu opsi populer. Lihatlah konsep dan pola yang digunakan karena sering kali merupakan cara yang baik untuk mempelajari potensi masalah yang mungkin Anda hadapi dalam aplikasi web besar dan bagaimana cara menyelesaikannya.
### Tugas
@ -75,9 +75,9 @@ let state = {
};
```
Idenya adalah untuk *menyentralisasi* semua data aplikasi kita dalam satu objek state. Saat ini kita hanya memiliki `account` dalam state sehingga tidak banyak berubah, tetapi ini menciptakan jalur untuk evolusi.
Idenya adalah untuk *memusatkan* semua data aplikasi kami dalam satu objek state. Saat ini kami hanya memiliki `account` dalam state sehingga tidak banyak berubah, tetapi ini menciptakan jalur untuk evolusi.
Kita juga harus memperbarui fungsi yang menggunakannya. Dalam fungsi `register()` dan `login()`, ganti `account = ...` dengan `state.account = ...`;
Kami juga harus memperbarui fungsi yang menggunakannya. Dalam fungsi `register()` dan `login()`, ganti `account = ...` dengan `state.account = ...`;
Di bagian atas fungsi `updateDashboard()`, tambahkan baris ini:
@ -87,15 +87,15 @@ const account = state.account;
Refaktor ini sendiri tidak membawa banyak peningkatan, tetapi idenya adalah untuk meletakkan dasar bagi perubahan berikutnya.
## Melacak Perubahan Data
## Melacak perubahan data
Sekarang kita telah menempatkan objek `state` untuk menyimpan data kita, langkah berikutnya adalah menyentralisasi pembaruan. Tujuannya adalah untuk mempermudah melacak setiap perubahan dan kapan perubahan itu terjadi.
Sekarang setelah kami menempatkan objek `state` untuk menyimpan data kami, langkah berikutnya adalah memusatkan pembaruan. Tujuannya adalah untuk mempermudah melacak setiap perubahan dan kapan perubahan itu terjadi.
Untuk menghindari perubahan yang dilakukan pada objek `state`, juga merupakan praktik yang baik untuk menganggapnya [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), yang berarti bahwa objek tersebut tidak dapat dimodifikasi sama sekali. Ini juga berarti bahwa Anda harus membuat objek state baru jika Anda ingin mengubah apa pun di dalamnya. Dengan melakukan ini, Anda membangun perlindungan terhadap [efek samping](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) yang mungkin tidak diinginkan, dan membuka kemungkinan untuk fitur baru dalam aplikasi Anda seperti menerapkan undo/redo, sambil juga mempermudah debugging. Misalnya, Anda dapat mencatat setiap perubahan yang dilakukan pada state dan menyimpan riwayat perubahan untuk memahami sumber bug.
Dalam JavaScript, Anda dapat menggunakan [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) untuk membuat versi immutable dari sebuah objek. Jika Anda mencoba membuat perubahan pada objek immutable, sebuah pengecualian akan muncul.
Dalam JavaScript, Anda dapat menggunakan [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) untuk membuat versi objek yang tidak dapat diubah. Jika Anda mencoba membuat perubahan pada objek yang tidak dapat diubah, sebuah pengecualian akan muncul.
✅ Apakah Anda tahu perbedaan antara objek immutable *shallow* dan *deep*? Anda dapat membacanya [di sini](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Apakah Anda tahu perbedaan antara objek *shallow* dan *deep* yang tidak dapat diubah? Anda dapat membacanya [di sini](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Tugas
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
Dalam fungsi ini, kita membuat objek state baru dan menyalin data dari state sebelumnya menggunakan [*operator spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kemudian kita menimpa properti tertentu dari objek state dengan data baru menggunakan [notasi bracket](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` untuk penugasan. Akhirnya, kita mengunci objek untuk mencegah modifikasi menggunakan `Object.freeze()`. Saat ini kita hanya memiliki properti `account` yang disimpan dalam state, tetapi dengan pendekatan ini Anda dapat menambahkan sebanyak mungkin properti yang Anda butuhkan dalam state.
Dalam fungsi ini, kami membuat objek state baru dan menyalin data dari state sebelumnya menggunakan [*operator spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kemudian kami menimpa properti tertentu dari objek state dengan data baru menggunakan [notasi kurung](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` untuk penugasan. Akhirnya, kami mengunci objek untuk mencegah modifikasi menggunakan `Object.freeze()`. Saat ini kami hanya memiliki properti `account` yang disimpan dalam state, tetapi dengan pendekatan ini Anda dapat menambahkan sebanyak mungkin properti yang Anda butuhkan dalam state.
Kita juga akan memperbarui inisialisasi `state` untuk memastikan state awal juga dibekukan:
Kami juga akan memperbarui inisialisasi `state` untuk memastikan state awal juga dibekukan:
```js
let state = Object.freeze({
@ -132,7 +132,7 @@ Lakukan hal yang sama dengan fungsi `login`, mengganti `state.account = data;` d
updateState('account', data);
```
Sekarang kita akan memperbaiki masalah data akun yang tidak dibersihkan ketika pengguna mengklik *Logout*.
Kami sekarang akan mengambil kesempatan untuk memperbaiki masalah data akun yang tidak dibersihkan ketika pengguna mengklik *Logout*.
Buat fungsi baru `logout()`:
@ -145,13 +145,13 @@ function logout() {
Dalam `updateDashboard()`, ganti pengalihan `return navigate('/login');` dengan `return logout()`;
Coba daftarkan akun baru, keluar, dan masuk lagi untuk memeriksa apakah semuanya masih berfungsi dengan benar.
Cobalahmendaftarkan akun baru, keluar, dan masuk lagi untuk memeriksa apakah semuanya masih berfungsi dengan benar.
> Tip: Anda dapat melihat semua perubahan state dengan menambahkan `console.log(state)` di bagian bawah `updateState()` dan membuka konsol di alat pengembangan browser Anda.
## Mempertahankan State
## Mempertahankan state
Sebagian besar aplikasi web perlu mempertahankan data agar dapat berfungsi dengan benar. Semua data penting biasanya disimpan di database dan diakses melalui server API, seperti data akun pengguna dalam kasus kita. Tetapi terkadang, juga menarik untuk mempertahankan beberapa data di aplikasi klien yang berjalan di browser Anda, untuk pengalaman pengguna yang lebih baik atau untuk meningkatkan kinerja pemuatan.
Sebagian besar aplikasi web perlu mempertahankan data agar dapat berfungsi dengan benar. Semua data penting biasanya disimpan di database dan diakses melalui server API, seperti data akun pengguna dalam kasus kami. Tetapi terkadang, juga menarik untuk mempertahankan beberapa data di aplikasi klien yang berjalan di browser Anda, untuk pengalaman pengguna yang lebih baik atau untuk meningkatkan kinerja pemuatan.
Ketika Anda ingin mempertahankan data di browser Anda, ada beberapa pertanyaan penting yang harus Anda tanyakan pada diri sendiri:
@ -163,15 +163,15 @@ Ada beberapa cara untuk menyimpan informasi di dalam aplikasi web, tergantung pa
Opsi lain adalah menggunakan salah satu dari banyak API browser untuk menyimpan data. Dua di antaranya sangat menarik:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): sebuah [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) yang memungkinkan untuk mempertahankan data spesifik untuk situs web saat ini di antara sesi yang berbeda. Data yang disimpan di dalamnya tidak pernah kedaluwarsa.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ini bekerja sama seperti `localStorage` kecuali bahwa data yang disimpan di dalamnya dihapus saat sesi berakhir (ketika browser ditutup).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): yang satu ini bekerja sama seperti `localStorage` kecuali bahwa data yang disimpan di dalamnya dihapus saat sesi berakhir (saat browser ditutup).
Perlu dicatat bahwa kedua API ini hanya memungkinkan untuk menyimpan [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Jika Anda ingin menyimpan objek kompleks, Anda perlu menyerialkannya ke format [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) menggunakan [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Jika Anda ingin membuat aplikasi web yang tidak bekerja dengan server, juga memungkinkan untuk membuat database di klien menggunakan API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ini disediakan untuk kasus penggunaan lanjutan atau jika Anda perlu menyimpan jumlah data yang signifikan, karena lebih kompleks untuk digunakan.
✅ Jika Anda ingin membuat aplikasi web yang tidak bekerja dengan server, juga memungkinkan untuk membuat database di klien menggunakan API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Yang satu ini disediakan untuk kasus penggunaan lanjutan atau jika Anda perlu menyimpan sejumlah besar data, karena lebih kompleks untuk digunakan.
### Tugas
Kita ingin pengguna tetap masuk sampai mereka secara eksplisit mengklik tombol *Logout*, jadi kita akan menggunakan `localStorage` untuk menyimpan data akun. Pertama, mari kita tentukan kunci yang akan kita gunakan untuk menyimpan data kita.
Kami ingin pengguna kami tetap masuk hingga mereka secara eksplisit mengklik tombol *Logout*, jadi kami akan menggunakan `localStorage` untuk menyimpan data akun. Pertama, mari kita tentukan kunci yang akan kita gunakan untuk menyimpan data kita.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Kemudian tambahkan baris ini di akhir fungsi `updateState()`:
Dengan ini, data akun pengguna akan dipertahankan dan selalu diperbarui karena sebelumnya kita telah menyentralisasi semua pembaruan state. Di sinilah kita mulai mendapatkan manfaat dari semua refaktor sebelumnya 🙂.
Dengan ini, data akun pengguna akan dipertahankan dan selalu diperbarui karena sebelumnya kami memusatkan semua pembaruan state kami. Di sinilah kami mulai mendapatkan manfaat dari semua refaktor sebelumnya 🙂.
Karena data disimpan, kita juga harus mengurus pemulihannya saat aplikasi dimuat. Karena kita akan mulai memiliki lebih banyak kode inisialisasi, mungkin ide yang baik untuk membuat fungsi baru `init`, yang juga mencakup kode sebelumnya di bagian bawah `app.js`:
Karena data disimpan, kami juga harus mengurus pemulihannya saat aplikasi dimuat. Karena kami akan mulai memiliki lebih banyak kode inisialisasi, mungkin ide yang baik untuk membuat fungsi baru `init`, yang juga mencakup kode sebelumnya di bagian bawah `app.js`:
```js
function init() {
@ -202,15 +202,15 @@ function init() {
init();
```
Di sini kita mengambil data yang disimpan, dan jika ada, kita memperbarui state sesuai. Penting untuk melakukan ini *sebelum* memperbarui rute, karena mungkin ada kode yang bergantung pada state selama pembaruan halaman.
Di sini kami mengambil data yang disimpan, dan jika ada, kami memperbarui state sesuai. Penting untuk melakukan ini *sebelum* memperbarui rute, karena mungkin ada kode yang bergantung pada state selama pembaruan halaman.
Kita juga dapat menjadikan halaman *Dashboard* sebagai halaman default aplikasi kita, karena sekarang kita mempertahankan data akun. Jika tidak ada data yang ditemukan, dashboard akan mengurus pengalihan ke halaman *Login* bagaimanapun. Dalam `updateRoute()`, ganti fallback `return navigate('/login');` dengan `return navigate('/dashboard');`.
Kami juga dapat menjadikan halaman *Dashboard* sebagai halaman default aplikasi kami, karena sekarang kami mempertahankan data akun. Jika tidak ada data yang ditemukan, dashboard akan mengurus pengalihan ke halaman *Login* bagaimanapun. Dalam `updateRoute()`, ganti fallback `return navigate('/login');` dengan `return navigate('/dashboard');`.
Sekarang masuk ke aplikasi dan coba segarkan halaman. Anda seharusnya tetap berada di dashboard. Dengan pembaruan itu kita telah menangani semua masalah awal kita...
Sekarang masuk ke aplikasi dan coba segarkan halaman. Anda seharusnya tetap berada di dashboard. Dengan pembaruan itu kami telah mengatasi semua masalah awal kami...
## Menyegarkan Data
## Menyegarkan data
...Tetapi kita mungkin juga telah menciptakan masalah baru. Ups!
...Tetapi kami mungkin juga telah menciptakan masalah baru. Ups!
Pergi ke dashboard menggunakan akun `test`, lalu jalankan perintah ini di terminal untuk membuat transaksi baru:
Coba segarkan halaman dashboard di browser sekarang. Apa yang terjadi? Apakah Anda melihat transaksi baru?
Coba segarkan halaman dashboard Anda di browser sekarang. Apa yang terjadi? Apakah Anda melihat transaksi baru?
State dipertahankan tanpa batas waktu berkat `localStorage`, tetapi itu juga berarti tidak pernah diperbarui sampai Anda keluar dari aplikasi dan masuk lagi!
State dipertahankan tanpa batas waktu berkat `localStorage`, tetapi itu juga berarti tidak pernah diperbarui hingga Anda keluar dari aplikasi dan masuk lagi!
Salah satu strategi yang mungkin untuk memperbaikinya adalah memuat ulang data akun setiap kali dashboard dimuat, untuk menghindari data yang tidak diperbarui.
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Metode ini memeriksa bahwa kita saat ini masuk lalu memuat ulang data akun dari server.
Metode ini memeriksa bahwa kami saat ini masuk lalu memuat ulang data akun dari server.
Buat fungsi lain bernama `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Yang satu ini memperbarui data akun, lalu mengurus pembaruan HTML halaman dashboard. Ini adalah apa yang perlu kita panggil ketika rute dashboard dimuat. Perbarui definisi rute dengan:
Yang satu ini memperbarui data akun, lalu mengurus pembaruan HTML halaman dashboard. Ini adalah apa yang perlu kita panggil saat rute dashboard dimuat. Perbarui definisi rute dengan:
```js
const routes = {
@ -273,22 +273,22 @@ Coba muat ulang dashboard sekarang, seharusnya menampilkan data akun yang diperb
## 🚀 Tantangan
Sekarang kita memuat ulang data akun setiap kali dashboard dimuat, apakah menurut Anda kita masih perlu mempertahankan *semua data akun*?
Sekarang setelah kami memuat ulang data akun setiap kali dashboard dimuat, apakah menurut Anda kami masih perlu mempertahankan *semua data akun*?
Cobalah bekerja sama untuk mengubah apa yang disimpan dan dimuat dari `localStorage` agar hanya mencakup apa yang benar-benar diperlukan untuk aplikasi berfungsi.
Berikut adalah contoh hasil setelah menyelesaikan tugas:


---
**Penafian**:
Dokumen ini telah diterjemahkan menggunakan layanan penerjemahan AI [Co-op Translator](https://github.com/Azure/co-op-translator). Meskipun kami berupaya untuk memberikan hasil yang akurat, harap diperhatikan bahwa terjemahan otomatis mungkin mengandung kesalahan atau ketidakakuratan. Dokumen asli dalam bahasa aslinya harus dianggap sebagai sumber yang berwenang. Untuk informasi yang bersifat kritis, disarankan menggunakan jasa penerjemahan manusia profesional. Kami tidak bertanggung jawab atas kesalahpahaman atau penafsiran yang keliru yang timbul dari penggunaan terjemahan ini.
Dokumen ini telah diterjemahkan menggunakan layanan terjemahan AI [Co-op Translator](https://github.com/Azure/co-op-translator). Meskipun kami berupaya untuk memberikan hasil yang akurat, harap diperhatikan bahwa terjemahan otomatis mungkin mengandung kesalahan atau ketidakakuratan. Dokumen asli dalam bahasa aslinya harus dianggap sebagai sumber yang berwenang. Untuk informasi yang bersifat kritis, disarankan menggunakan jasa penerjemah manusia profesional. Kami tidak bertanggung jawab atas kesalahpahaman atau penafsiran yang keliru yang timbul dari penggunaan terjemahan ini.
Man mano che un'applicazione web cresce, diventa una sfida tenere traccia di tutti i flussi di dati. Quale codice ottiene i dati, quale pagina li utilizza, dove e quando devono essere aggiornati... è facile finire con un codice disordinato e difficile da mantenere. Questo è particolarmente vero quando è necessario condividere dati tra diverse pagine della tua app, ad esempio i dati dell'utente. Il concetto di *gestione dello stato* è sempre esistito in tutti i tipi di programmi, ma con l'aumento della complessità delle app web è diventato un punto chiave da considerare durante lo sviluppo.
Man mano che un'applicazione web cresce, diventa una sfida tenere traccia di tutti i flussi di dati. Quale codice ottiene i dati, quale pagina li utilizza, dove e quando devono essere aggiornati... è facile finire con un codice disordinato e difficile da mantenere. Questo è particolarmente vero quando è necessario condividere dati tra diverse pagine della tua app, ad esempio i dati dell'utente. Il concetto di *gestione dello stato* è sempre esistito in tutti i tipi di programmi, ma poiché le app web continuano a crescere in complessità, ora è un punto chiave da considerare durante lo sviluppo.
In questa parte finale, esamineremo l'app che abbiamo costruito per ripensare a come viene gestito lo stato, consentendo il supporto per l'aggiornamento del browser in qualsiasi momento e la persistenza dei dati tra le sessioni utente.
In questa parte finale, esamineremo l'app che abbiamo costruito per ripensare a come viene gestito lo stato, consentendo il supporto per il refresh del browser in qualsiasi momento e la persistenza dei dati tra le sessioni utente.
### Prerequisiti
Devi aver completato la parte relativa al [recupero dei dati](../3-data/README.md) dell'app web per questa lezione. Inoltre, devi installare [Node.js](https://nodejs.org) e [eseguire l'API del server](../api/README.md) localmente per poter gestire i dati dell'account.
Devi aver completato la parte [data fetching](../3-data/README.md) dell'app web per questa lezione. Inoltre, devi installare [Node.js](https://nodejs.org) e [eseguire l'API del server](../api/README.md) localmente per gestire i dati dell'account.
Puoi verificare che il server stia funzionando correttamente eseguendo questo comando in un terminale:
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## Ripensare la gestione dello stato
Nella [lezione precedente](../3-data/README.md), abbiamo introdotto un concetto di base di stato nella nostra app con la variabile globale `account` che contiene i dati bancari dell'utente attualmente connesso. Tuttavia, la nostra implementazione attuale presenta alcune lacune. Prova ad aggiornare la pagina quando sei nella dashboard. Cosa succede?
Nella [lezione precedente](../3-data/README.md), abbiamo introdotto un concetto di base di stato nella nostra app con la variabile globale `account` che contiene i dati bancari dell'utente attualmente loggato. Tuttavia, la nostra implementazione attuale presenta alcune lacune. Prova a ricaricare la pagina quando sei sulla dashboard. Cosa succede?
Ci sono 3 problemi con il codice attuale:
- Lo stato non è persistente, poiché un aggiornamento del browser ti riporta alla pagina di login.
- Ci sono più funzioni che modificano lo stato. Man mano che l'app cresce, può diventare difficile tenere traccia delle modifiche ed è facile dimenticare di aggiornare qualcosa.
- Lo stato non viene ripulito, quindi quando clicchi su *Logout* i dati dell'account sono ancora presenti anche se sei nella pagina di login.
- Lo stato non è persistente, poiché un refresh del browser ti riporta alla pagina di login.
- Ci sono molte funzioni che modificano lo stato. Man mano che l'app cresce, può diventare difficile tracciare i cambiamenti ed è facile dimenticare di aggiornare qualcosa.
- Lo stato non viene ripulito, quindi quando clicchi su *Logout* i dati dell'account sono ancora presenti anche se sei sulla pagina di login.
Potremmo aggiornare il nostro codice per affrontare questi problemi uno per uno, ma ciò creerebbe più duplicazione di codice e renderebbe l'app più complessa e difficile da mantenere. Oppure potremmo fermarci per qualche minuto e ripensare la nostra strategia.
> Quali problemi stiamo davvero cercando di risolvere qui?
> Quali problemi stiamo cercando di risolvere qui?
La [gestione dello stato](https://en.wikipedia.org/wiki/State_management) riguarda il trovare un buon approccio per risolvere questi due problemi particolari:
La [gestione dello stato](https://en.wikipedia.org/wiki/State_management) riguarda principalmente il trovare un buon approccio per risolvere questi due problemi particolari:
- Come mantenere i flussi di dati in un'app comprensibili?
- Come mantenere i dati dello stato sempre sincronizzati con l'interfaccia utente (e viceversa)?
Una volta affrontati questi problemi, qualsiasi altra questione potrebbe essere già risolta o diventare più facile da risolvere. Esistono molti approcci possibili per affrontare questi problemi, ma utilizzeremo una soluzione comune che consiste nel **centralizzare i dati e i modi per modificarli**. I flussi di dati funzionerebbero così:
Una volta affrontati questi problemi, qualsiasi altro problema che potresti avere potrebbe essere già risolto o diventare più facile da risolvere. Esistono molti approcci possibili per risolvere questi problemi, ma utilizzeremo una soluzione comune che consiste nel **centralizzare i dati e i modi per modificarli**. I flussi di dati seguirebbero questo schema:

> Qui non tratteremo la parte in cui i dati aggiornano automaticamente la vista, poiché è legata a concetti più avanzati di [Programmazione Reattiva](https://en.wikipedia.org/wiki/Reactive_programming). È un buon argomento di approfondimento se vuoi esplorare ulteriormente.
> Qui non tratteremo la parte in cui i dati aggiornano automaticamente la vista, poiché è legata a concetti più avanzati di [Programmazione Reattiva](https://en.wikipedia.org/wiki/Reactive_programming). È un buon argomento di approfondimento se sei interessato a un'immersione più profonda.
✅ Esistono molte librerie con approcci diversi alla gestione dello stato, [Redux](https://redux.js.org) essendo una delle opzioni più popolari. Dai un'occhiata ai concetti e ai pattern utilizzati, poiché spesso è un buon modo per imparare quali problemi potresti affrontare nelle grandi app web e come possono essere risolti.
✅ Esistono molte librerie con approcci diversi alla gestione dello stato, [Redux](https://redux.js.org) essendo una delle opzioni più popolari. Dai un'occhiata ai concetti e ai pattern utilizzati, poiché spesso è un buon modo per imparare quali problemi potresti affrontare in grandi app web e come possono essere risolti.
### Compito
@ -75,7 +75,7 @@ let state = {
};
```
L'idea è di *centralizzare* tutti i dati della nostra app in un unico oggetto di stato. Per ora abbiamo solo `account` nello stato, quindi non cambia molto, ma crea una base per le evoluzioni future.
L'idea è di *centralizzare* tutti i dati della nostra app in un unico oggetto stato. Per ora abbiamo solo `account` nello stato, quindi non cambia molto, ma crea una base per evoluzioni future.
Dobbiamo anche aggiornare le funzioni che lo utilizzano. Nelle funzioni `register()` e `login()`, sostituisci `account = ...` con `state.account = ...`;
@ -87,15 +87,15 @@ const account = state.account;
Questo refactoring di per sé non ha portato grandi miglioramenti, ma l'idea era di gettare le basi per i prossimi cambiamenti.
## Tracciare le modifiche ai dati
## Tracciare i cambiamenti dei dati
Ora che abbiamo messo in atto l'oggetto `state` per memorizzare i nostri dati, il passo successivo è centralizzare gli aggiornamenti. L'obiettivo è rendere più facile tenere traccia di eventuali modifiche e quando avvengono.
Ora che abbiamo messo in atto l'oggetto `state` per memorizzare i nostri dati, il passo successivo è centralizzare gli aggiornamenti. L'obiettivo è rendere più facile tenere traccia di qualsiasi cambiamento e quando avviene.
Per evitare che vengano apportate modifiche all'oggetto `state`, è anche una buona pratica considerarlo [*immutabile*](https://en.wikipedia.org/wiki/Immutable_object), il che significa che non può essere modificato affatto. Significa anche che devi creare un nuovo oggetto di stato se vuoi cambiare qualcosa in esso. Facendo ciò, costruisci una protezione contro potenziali [effetti collaterali indesiderati](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) e apri possibilità per nuove funzionalità nella tua app, come implementare undo/redo, rendendo anche più facile il debug. Ad esempio, potresti registrare ogni modifica apportata allo stato e mantenere una cronologia delle modifiche per capire l'origine di un bug.
Per evitare che vengano apportate modifiche all'oggetto `state`, è anche una buona pratica considerarlo [*immutabile*](https://en.wikipedia.org/wiki/Immutable_object), il che significa che non può essere modificato affatto. Ciò significa anche che devi creare un nuovo oggetto stato se vuoi cambiare qualcosa in esso. Facendo così, costruisci una protezione contro potenziali [effetti collaterali](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) indesiderati e apri possibilità per nuove funzionalità nella tua app, come implementare undo/redo, rendendo anche più facile il debug. Ad esempio, potresti registrare ogni cambiamento fatto allo stato e mantenere una cronologia dei cambiamenti per capire la fonte di un bug.
In JavaScript, puoi utilizzare [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) per creare una versione immutabile di un oggetto. Se provi a modificare un oggetto immutabile, verrà generata un'eccezione.
In JavaScript, puoi usare [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) per creare una versione immutabile di un oggetto. Se provi a fare modifiche a un oggetto immutabile, verrà sollevata un'eccezione.
✅ Conosci la differenza tra un oggetto immutabile *superficiale* e uno *profondo*? Puoi leggerne di più [qui](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Conosci la differenza tra un oggetto immutabile *superficiale* e uno *profondo*? Puoi leggere a riguardo [qui](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Compito
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
In questa funzione, stiamo creando un nuovo oggetto di stato e copiando i dati dallo stato precedente utilizzando lo [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Poi sovrascriviamo una particolare proprietà dell'oggetto di stato con i nuovi dati utilizzando la [notazione a parentesi](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` per l'assegnazione. Infine, blocchiamo l'oggetto per impedire modifiche utilizzando `Object.freeze()`. Per ora abbiamo solo la proprietà `account` memorizzata nello stato, ma con questo approccio puoi aggiungere tutte le proprietà di cui hai bisogno.
In questa funzione, stiamo creando un nuovo oggetto stato e copiando i dati dallo stato precedente usando l'[operatore spread (`...`)](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Poi sovrascriviamo una particolare proprietà dell'oggetto stato con i nuovi dati usando la [notazione a parentesi](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` per l'assegnazione. Infine, blocchiamo l'oggetto per prevenire modifiche usando `Object.freeze()`. Per ora abbiamo solo la proprietà `account` memorizzata nello stato, ma con questo approccio puoi aggiungere tutte le proprietà di cui hai bisogno nello stato.
Aggiorneremo anche l'inizializzazione dello `state` per assicurarci che lo stato iniziale sia anch'esso bloccato:
Aggiorneremo anche l'inizializzazione dello stato per assicurarci che lo stato iniziale sia anch'esso bloccato:
```js
let state = Object.freeze({
@ -145,33 +145,33 @@ function logout() {
In `updateDashboard()`, sostituisci il reindirizzamento `return navigate('/login');` con `return logout()`;
Prova a registrare un nuovo account, a fare logout e a rientrare per verificare che tutto funzioni ancora correttamente.
Prova a registrare un nuovo account, a disconnetterti e a riconnetterti per verificare che tutto funzioni correttamente.
> Suggerimento: puoi dare un'occhiata a tutte le modifiche dello stato aggiungendo `console.log(state)` alla fine di `updateState()` e aprendo la console negli strumenti di sviluppo del browser.
> Suggerimento: puoi dare un'occhiata a tutti i cambiamenti dello stato aggiungendo `console.log(state)` alla fine di `updateState()` e aprendo la console negli strumenti di sviluppo del tuo browser.
## Persistenza dello stato
La maggior parte delle app web ha bisogno di persistere i dati per funzionare correttamente. Tutti i dati critici sono solitamente memorizzati in un database e accessibili tramite un'API del server, come i dati dell'account utente nel nostro caso. Ma a volte, è anche interessante persistere alcuni dati nell'app client che gira nel browser, per migliorare l'esperienza utente o le prestazioni di caricamento.
La maggior parte delle app web ha bisogno di persistere i dati per funzionare correttamente. Tutti i dati critici sono solitamente memorizzati in un database e accessibili tramite un'API del server, come i dati dell'account utente nel nostro caso. Ma a volte, è anche interessante persistere alcuni dati nell'app client che gira nel tuo browser, per una migliore esperienza utente o per migliorare le prestazioni di caricamento.
Quando vuoi persistere i dati nel browser, ci sono alcune domande importanti che dovresti porti:
Quando vuoi persistere i dati nel tuo browser, ci sono alcune domande importanti che dovresti porti:
- *I dati sono sensibili?* Dovresti evitare di memorizzare dati sensibili sul client, come le password degli utenti.
- *Per quanto tempo hai bisogno di conservare questi dati?*Vuoi accedere a questi dati solo per la sessione corrente o vuoi che siano memorizzati per sempre?
- *Per quanto tempo hai bisogno di conservare questi dati?*Intendi accedere a questi dati solo per la sessione corrente o vuoi che siano memorizzati per sempre?
Esistono diversi modi per memorizzare informazioni all'interno di un'app web, a seconda di ciò che vuoi ottenere. Ad esempio, puoi utilizzare gli URL per memorizzare una query di ricerca e renderla condivisibile tra gli utenti. Puoi anche utilizzare i [cookie HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) se i dati devono essere condivisi con il server, come le informazioni di [autenticazione](https://en.wikipedia.org/wiki/Authentication).
Un'altra opzione è utilizzare una delle tante API del browser per memorizzare i dati. Due di esse sono particolarmente interessanti:
Un'altra opzione è utilizzare una delle molte API del browser per memorizzare i dati. Due di queste sono particolarmente interessanti:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): un [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) che consente di persistere i dati specifici del sito web corrente tra diverse sessioni. I dati salvati in esso non scadono mai.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): un [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) che consente di persistere dati specifici per il sito web corrente tra diverse sessioni. I dati salvati in esso non scadono mai.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funziona allo stesso modo di `localStorage` tranne per il fatto che i dati memorizzati in esso vengono cancellati quando la sessione termina (quando il browser viene chiuso).
Nota che entrambe queste API consentono solo di memorizzare [stringhe](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Se vuoi memorizzare oggetti complessi, dovrai serializzarli nel formato [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) utilizzando [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Nota che entrambe queste API consentono solo di memorizzare [stringhe](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Se vuoi memorizzare oggetti complessi, dovrai serializzarli nel formato [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) usando [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Se vuoi creare un'app web che non funziona con un server, è anche possibile creare un database sul client utilizzando l'API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Questa è riservata a casi d'uso avanzati o se hai bisogno di memorizzare una quantità significativa di dati, poiché è più complessa da utilizzare.
### Compito
Vogliamo che i nostri utenti rimangano connessi fino a quando non cliccano esplicitamente sul pulsante *Logout*, quindi utilizzeremo `localStorage` per memorizzare i dati dell'account. Per prima cosa, definiamo una chiave che utilizzeremo per memorizzare i nostri dati.
Vogliamo che i nostri utenti rimangano loggati fino a quando non cliccano esplicitamente sul pulsante *Logout*, quindi utilizzeremo `localStorage` per memorizzare i dati dell'account. Per prima cosa, definiamo una chiave che utilizzeremo per memorizzare i nostri dati.
Con questo, i dati dell'account utente saranno persistenti e sempre aggiornati poiché abbiamo centralizzato in precedenza tutti gli aggiornamenti dello stato. È qui che iniziamo a beneficiare di tutti i nostri refactoring precedenti 🙂.
Poiché i dati vengono salvati, dobbiamo anche occuparci di ripristinarli quando l'app viene caricata. Poiché inizieremo ad avere più codice di inizializzazione, potrebbe essere una buona idea creare una nuova funzione `init`, che includa anche il nostro codice precedente in fondo a`app.js`:
Poiché i dati vengono salvati, dobbiamo anche occuparci di ripristinarli quando l'app viene caricata. Poiché inizieremo ad avere più codice di inizializzazione, potrebbe essere una buona idea creare una nuova funzione `init`, che includa anche il nostro codice precedente alla fine di`app.js`:
```js
function init() {
@ -202,15 +202,15 @@ function init() {
init();
```
Qui recuperiamo i dati salvati e, se presenti, aggiorniamo lo stato di conseguenza. È importante farlo *prima* di aggiornare la rotta, poiché potrebbe esserci del codice che si basa sullo stato durante l'aggiornamento della pagina.
Qui recuperiamo i dati salvati e, se ci sono, aggiorniamo lo stato di conseguenza. È importante farlo *prima* di aggiornare la rotta, poiché potrebbe esserci codice che si basa sullo stato durante l'aggiornamento della pagina.
Possiamo anche rendere la pagina *Dashboard* la pagina predefinita della nostra applicazione, poiché ora stiamo persistendo i dati dell'account. Se non vengono trovati dati, la dashboard si occupa di reindirizzare alla pagina di *Login* comunque. In `updateRoute()`, sostituisci il fallback `return navigate('/login');` con `return navigate('/dashboard');`.
Possiamo anche rendere la pagina *Dashboard* la pagina predefinita della nostra applicazione, poiché ora stiamo persistendo i dati dell'account. Se non vengono trovati dati, la dashboard si occupa di reindirizzare alla pagina *Login* comunque. In `updateRoute()`, sostituisci il fallback `return navigate('/login');` con `return navigate('/dashboard');`.
Ora accedi all'app e prova ad aggiornare la pagina. Dovresti rimanere sulla dashboard. Con questo aggiornamento abbiamo risolto tutti i nostri problemi iniziali...
Ora accedi all'app e prova a ricaricare la pagina. Dovresti rimanere sulla dashboard. Con questo aggiornamento abbiamo affrontato tutti i nostri problemi iniziali...
## Aggiornare i dati
...Ma potremmo anche averne creato uno nuovo. Ops!
...Ma potremmo anche averne creato uno nuovo. Oops!
Vai alla dashboard utilizzando l'account `test`, quindi esegui questo comando su un terminale per creare una nuova transazione:
Prova ad aggiornare la pagina della dashboard nel browser ora. Cosa succede? Vedi la nuova transazione?
Prova a ricaricare la pagina della dashboard nel browser ora. Cosa succede? Vedi la nuova transazione?
Lo stato è persistente indefinitamente grazie a `localStorage`, ma ciò significa anche che non viene mai aggiornato fino a quando non esci dall'app e accedi di nuovo!
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Questo metodo verifica che siamo attualmente connessi, quindi ricarica i dati dell'account dal server.
Questo metodo verifica che siamo attualmente loggati e poi ricarica i dati dell'account dal server.
Crea un'altra funzione chiamata `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Questa aggiorna i dati dell'account, quindi si occupa di aggiornare l'HTML della pagina della dashboard. È ciò che dobbiamo chiamare quando la rotta della dashboard viene caricata. Aggiorna la definizione della rotta con:
Questa funzione aggiorna i dati dell'account e poi si occupa di aggiornare l'HTML della pagina dashboard. È ciò che dobbiamo chiamare quando la rotta della dashboard viene caricata. Aggiorna la definizione della rotta con:
```js
const routes = {
@ -267,7 +267,7 @@ const routes = {
};
```
Prova ad aggiornare la dashboard ora, dovrebbe mostrare i dati dell'account aggiornati.
Prova a ricaricare la dashboard ora, dovrebbe mostrare i dati dell'account aggiornati.
---
@ -275,20 +275,20 @@ Prova ad aggiornare la dashboard ora, dovrebbe mostrare i dati dell'account aggi
Ora che ricarichiamo i dati dell'account ogni volta che la dashboard viene caricata, pensi che sia ancora necessario persistere *tutti i dati dell'account*?
Prova a lavorare insieme per modificare ciò che viene salvato e caricato da `localStorage`per includere solo ciò che è assolutamente necessario per il funzionamento dell'app.
Prova a lavorare insieme per modificare ciò che viene salvato e caricato da `localStorage`includendo solo ciò che è assolutamente necessario per il funzionamento dell'app.
[Implementa la finestra di dialogo "Aggiungi transazione"](assignment.md)
Ecco un esempio del risultato dopo aver completato l'assegnazione:
[Implementare la finestra di dialogo "Aggiungi transazione"](assignment.md)
Ecco un esempio del risultato dopo aver completato il compito:

---
**Disclaimer**:
Questo documento è stato tradotto utilizzando il servizio di traduzione automatica [Co-op Translator](https://github.com/Azure/co-op-translator). Sebbene ci impegniamo per garantire l'accuratezza, si prega di notare che le traduzioni automatiche potrebbero contenere errori o imprecisioni. Il documento originale nella sua lingua nativa dovrebbe essere considerato la fonte autorevole. Per informazioni critiche, si consiglia una traduzione professionale eseguita da un traduttore umano. Non siamo responsabili per eventuali fraintendimenti o interpretazioni errate derivanti dall'uso di questa traduzione.
**Disclaimer (Avvertenza)**:
Questo documento è stato tradotto utilizzando il servizio di traduzione automatica [Co-op Translator](https://github.com/Azure/co-op-translator). Sebbene ci impegniamo per garantire l'accuratezza, si prega di notare che le traduzioni automatiche possono contenere errori o imprecisioni. Il documento originale nella sua lingua nativa dovrebbe essere considerato la fonte autorevole. Per informazioni critiche, si raccomanda una traduzione professionale umana. Non siamo responsabili per eventuali incomprensioni o interpretazioni errate derivanti dall'uso di questa traduzione.
웹 애플리케이션이 커지면 모든 데이터 흐름을 추적하는 것이 점점 어려워집니다. 어떤 코드가 데이터를 가져오는지, 어떤 페이지가 데이터를 사용하는지, 어디서 그리고 언제 업데이트가 필요한지... 결국 유지 관리가 어려운 복잡한 코드로 이어지기 쉽습니다. 특히 사용자 데이터를 앱의 여러 페이지에서 공유해야 할 때 더욱 그렇습니다. *상태 관리*라는 개념은 모든 종류의 프로그램에서 항상 존재해 왔지만, 웹 앱이 점점 복잡해짐에 따라 개발 중에 반드시 고려해야 할 핵심 요소가 되었습니다.
웹 애플리케이션이 성장함에 따라 모든 데이터 흐름을 추적하는 것이 점점 어려워집니다. 어떤 코드가 데이터를 가져오고, 어떤 페이지가 데이터를 소비하며, 어디서 언제 업데이트가 필요한지... 코드가 복잡해지고 유지 관리가 어려워지기 쉽습니다. 특히 앱의 여러 페이지에서 데이터를 공유해야 할 때, 예를 들어 사용자 데이터를 공유해야 할 때 더욱 그렇습니다. *상태 관리*라는 개념은 모든 종류의 프로그램에서 항상 존재해 왔지만, 웹 앱이 점점 복잡해짐에 따라 개발 중에 반드시 고려해야 할 핵심 요소가 되었습니다.
이 마지막 부분에서는 우리가 만든 앱을 다시 살펴보고 상태를 관리하는 방법을 재구성하여 브라우저 새로고침을 지원하고 사용자 세션 간 데이터를 지속적으로 유지할 수 있도록 합니다.
이 마지막 부분에서는 우리가 만든 앱을 다시 살펴보고 상태를 관리하는 방법을 재구성하여 브라우저 새로고침을 지원하고 사용자 세션 간 데이터를 지속적으로 유지할 수 있도록 할 것입니다.
### 사전 요구 사항
이 강의를 위해 웹 앱의 [데이터 가져오기](../3-data/README.md) 부분을 완료해야 합니다. 또한 [Node.js](https://nodejs.org)를 설치하고 [서버 API](../api/README.md)를 로컬에서 실행하여 계정 데이터를 관리할 수 있어야 합니다.
이 강의를 위해 웹 앱의 [데이터 가져오기](../3-data/README.md) 부분을 완료해야 합니다. 또한 [Node.js](https://nodejs.org)를 설치하고 [서버 API를 실행](../api/README.md)하여 계정 데이터를 관리할 수 있어야 합니다.
터미널에서 다음 명령을 실행하여 서버가 제대로 실행되고 있는지 확인할 수 있습니다:
서버가 제대로 실행되고 있는지 확인하려면 터미널에서 다음 명령을 실행하세요:
```sh
curl http://localhost:5000/api
@ -34,15 +34,15 @@ curl http://localhost:5000/api
## 상태 관리 재구성
[이전 강의](../3-data/README.md)에서는 현재 로그인한 사용자의 은행 데이터를 포함하는 전역 `account` 변수를 사용하여 앱에서 상태의 기본 개념을 소개했습니다. 하지만 현재 구현에는 몇 가지 결함이 있습니다. 대시보드 페이지에서 새로고침을 시도해 보세요. 어떤 일이 발생하나요?
[이전 강의](../3-data/README.md)에서는 현재 로그인한 사용자의 은행 데이터를 포함하는 전역 `account` 변수를 사용하여 앱에서 상태의 기본 개념을 소개했습니다. 하지만 현재 구현에는 몇 가지 결함이 있습니다. 대시보드에서 페이지를 새로고침해 보세요. 어떤 일이 발생하나요?
현재 코드에는 다음과 같은 세 가지 문제가 있습니다:
현재 코드에는 다음과 같은 3가지 문제가 있습니다:
- 상태가 지속되지 않아 브라우저를 새로고침하면 로그인 페이지로 돌아갑니다.
- 상태를 수정하는 함수가 여러 개 있습니다. 앱이 커지면 변경 사항을 추적하기 어려워지고 업데이트를 잊어버리기 쉽습니다.
- 상태가 정리되지 않아 *로그아웃*을 클릭해도 로그인 페이지에 있어도 계정 데이터가 그대로 남아 있습니다.
- 상태를 수정하는 함수가 여러 개 있습니다. 앱이 성장함에 따라 변경 사항을 추적하기 어려워지고 하나를 업데이트하는 것을 잊기 쉽습니다.
- 상태가 정리되지 않아 *로그아웃*을 클릭해도 로그인 페이지에 있지만 계정 데이터는 여전히 남아 있습니다.
이 문제를 하나씩 해결하기 위해 코드를 업데이트할 수도 있지만, 이는 코드 중복을 증가시키고 앱을 더 복잡하고 유지 관리하기 어렵게 만듭니다. 또는 몇 분 동안 멈추고 전략을 다시 생각해 볼 수도 있습니다.
이 문제를 하나씩 해결하기 위해 코드를 업데이트할 수도 있지만, 이는 코드 중복을 증가시키고 앱을 더 복잡하고 유지 관리하기 어렵게 만들 것입니다. 또는 몇 분 동안 멈추고 전략을 재구성할 수 있습니다.
> 여기서 우리가 실제로 해결하려는 문제는 무엇일까요?
@ -51,17 +51,17 @@ curl http://localhost:5000/api
- 앱의 데이터 흐름을 이해하기 쉽게 만드는 방법은 무엇인가?
- 상태 데이터와 사용자 인터페이스를 항상 동기화 상태로 유지하는 방법은 무엇인가?
이 두 가지를 해결하면 다른 문제는 이미 해결되었거나 해결하기 쉬워질 수 있습니다. 이러한 문제를 해결하기 위한 접근 방식은 다양하지만, 우리는 **데이터와 변경 방법을 중앙 집중화**하는 일반적인 솔루션을 선택할 것입니다. 데이터 흐름은 다음과 같이 진행됩니다:
이 문제를 해결하면 다른 문제는 이미 해결되었거나 해결하기 쉬워질 수 있습니다. 이러한 문제를 해결하기 위한 접근 방식은 다양하지만, 우리는 **데이터와 이를 변경하는 방법을 중앙 집중화**하는 일반적인 솔루션을 선택할 것입니다. 데이터 흐름은 다음과 같이 진행됩니다:

> 여기서는 데이터가 자동으로 뷰 업데이트를 트리거하는 부분을 다루지 않습니다. 이는 [반응형 프로그래밍](https://en.wikipedia.org/wiki/Reactive_programming)의 더 고급 개념과 관련이 있습니다. 깊이 있는 학습을 원한다면 좋은 후속 주제가 될 수 있습니다.
> 여기서는 데이터가 자동으로 뷰 업데이트를 트리거하는 부분을 다루지 않습니다. 이는 [반응형 프로그래밍](https://en.wikipedia.org/wiki/Reactive_programming)의 더 고급 개념과 관련이 있습니다. 깊이 있는 학습을 원한다면 좋은 후속 주제입니다.
✅ 상태 관리에 대한 다양한 접근 방식을 제공하는 많은 라이브러리가 있습니다. [Redux](https://redux.js.org)는 인기 있는 옵션 중 하나입니다. 대규모 웹 앱에서 직면할 수 있는 잠재적 문제와 이를 해결하는 방법을 배우는 데 유용한 개념과 패턴을 살펴보세요.
✅ 상태 관리에 대한 다양한 접근 방식을 가진 많은 라이브러리가 있으며, [Redux](https://redux.js.org)가 인기 있는 옵션입니다. 대규모 웹 앱에서 직면할 수 있는 잠재적 문제와 이를 해결하는 방법을 배우는 데 유용한 개념과 패턴을 살펴보세요.
### 작업
우리는 약간의 리팩토링으로 시작할 것입니다. `account` 선언을 다음으로 교체하세요:
약간의 리팩토링으로 시작해 보겠습니다. `account` 선언을 다음으로 교체하세요:
```js
let account = null;
@ -75,7 +75,7 @@ let state = {
};
```
우리 앱의 모든 데이터를 단일 상태 객체에 *중앙 집중화*하는 것이 아이디어입니다. 현재 상태에는 `account`만 있으므로 크게 달라지지 않지만, 향후 발전을 위한 길을 열어줍니다.
이 아이디어는 앱의 모든 데이터를 단일 상태 객체에 *중앙 집중화*하는 것입니다. 현재 상태에는 `account`만 있으므로 크게 변하지 않지만, 향후 발전을 위한 길을 열어줍니다.
이를 사용하는 함수도 업데이트해야 합니다. `register()` 및 `login()` 함수에서 `account = ...`를 `state.account = ...`로 교체하세요.
@ -91,15 +91,15 @@ const account = state.account;
이제 데이터를 저장할 `state` 객체를 설정했으니, 다음 단계는 업데이트를 중앙 집중화하는 것입니다. 목표는 변경 사항과 변경 시점을 더 쉽게 추적할 수 있도록 하는 것입니다.
`state` 객체를 변경하지 않도록 하는 것도 좋은 관행입니다. 이를 [*불변 객체*](https://en.wikipedia.org/wiki/Immutable_object)로 간주하여 전혀 수정할 수 없게 만드는 것입니다. 이는 상태 객체를 변경하려면 새 상태 객체를 생성해야 한다는 것을 의미합니다. 이렇게 하면 원치 않는 [부작용](https://en.wikipedia.org/wiki/Side_effect_(computer_science))을 방지하고, 앱에서 실행 취소/재실행 기능을 구현하거나 디버깅을 쉽게 만드는 등 새로운 기능을 추가할 가능성을 열어줍니다. 예를 들어, 상태에 가해진 모든 변경 사항을 기록하고 변경 내역을 유지하여 버그의 원인을 이해할 수 있습니다.
`state` 객체에 변경 사항이 발생하지 않도록 하기 위해, 이를 [*불변 객체*](https://en.wikipedia.org/wiki/Immutable_object)로 간주하는 것도 좋은 방법입니다. 즉, 전혀 수정할 수 없다는 뜻입니다. 또한, 상태 객체에서 무언가를 변경하려면 새 상태 객체를 생성해야 합니다. 이렇게 하면 원치 않는 [부작용](https://en.wikipedia.org/wiki/Side_effect_(computer_science))을 방지하고, 앱에서 실행 취소/재실행 기능을 구현하거나 디버깅을 쉽게 할 수 있는 새로운 기능을 추가할 가능성을 열어줍니다. 예를 들어, 상태에 발생한 모든 변경 사항을 기록하고 변경 내역을 유지하여 버그의 원인을 이해할 수 있습니다.
JavaScript에서는 [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)를 사용하여 객체의 불변 버전을 생성할 수 있습니다. 불변 객체를 변경하려고 하면 예외가 발생합니다.
JavaScript에서는 [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)를 사용하여 객체의 불변 버전을 생성할 수 있습니다. 불변 객체를 수정하려고 하면 예외가 발생합니다.
✅ *얕은* 불변 객체와 *깊은* 불변 객체의 차이를 알고 있나요? [여기](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)에서 읽어볼 수 있습니다.
### 작업
새로운 `updateState()` 함수를 만들어 보세요:
새로운 `updateState()` 함수를 만들어 보겠습니다:
```js
function updateState(property, newData) {
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
이 함수에서는 이전 상태에서 데이터를 복사하여 새 상태 객체를 생성합니다. 그런 다음 [대괄호 표기법](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties)을 사용하여 상태 객체의 특정 속성을 새 데이터로 덮어씁니다. 마지막으로 `Object.freeze()`를 사용하여 객체를 잠가 수정할 수 없게 만듭니다. 현재 상태에는 `account` 속성만 저장되어 있지만, 이 접근 방식을 사용하면 상태에 필요한 속성을 원하는 만큼 추가할 수 있습니다.
이 함수에서는 [*스프레드(`...`) 연산자*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)를 사용하여 이전 상태에서 데이터를 복사한 후, 상태 객체의 특정 속성을 새 데이터로 덮어씁니다. 그런 다음 `Object.freeze()`를 사용하여 객체를 잠그고 수정할 수 없도록 합니다. 현재 상태에는 `account` 속성만 저장되어 있지만, 이 접근 방식을 사용하면 상태에 필요한 속성을 원하는 만큼 추가할 수 있습니다.
초기 상태도 불변 상태로 설정되었는지 확인하기 위해 `state` 초기화를 업데이트하세요:
초기 상태도 불변 상태로 설정되도록 `state` 초기화를 업데이트합니다:
```js
let state = Object.freeze({
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
그런 다음 `register` 함수에서 `state.account = result;` 할당을 다음으로 교체하세요:
그런 다음,`register` 함수에서 `state.account = result;` 할당을 다음으로 교체하세요:
새 계정을 등록하고 로그아웃한 다음 다시 로그인하여 모든 것이 제대로 작동하는지 확인하세요.
새 계정을 등록하고 로그아웃한 후 다시 로그인하여 모든 것이 제대로 작동하는지 확인하세요.
> 팁: 브라우저 개발 도구의 콘솔을 열고 `updateState()` 하단에 `console.log(state)`를 추가하여 모든 상태 변경 사항을 확인할 수 있습니다.
## 상태 지속성
대부분의 웹 앱은 올바르게 작동하기 위해 데이터를 지속적으로 저장해야 합니다. 모든 중요한 데이터는 일반적으로 데이터베이스에 저장되고 서버 API를 통해 액세스됩니다. 예를 들어, 사용자 계정 데이터가 그렇습니다. 하지만 때로는 브라우저에서 실행 중인 클라이언트 앱에 일부 데이터를 저장하는 것이 사용자 경험을 개선하거나 로딩 성능을 향상시키는 데 유용할 수 있습니다.
대부분의 웹 앱은 올바르게 작동하기 위해 데이터를 지속적으로 저장해야 합니다. 모든 중요한 데이터는 일반적으로 데이터베이스에 저장되고 서버 API를 통해 액세스됩니다. 예를 들어, 사용자 계정 데이터가 그렇습니다. 하지만 때로는 브라우저에서 실행 중인 클라이언트 앱에 데이터를 저장하는 것도 사용자 경험을 개선하거나 로딩 성능을 향상시키는 데 유용합니다.
브라우저에서 데이터를 저장하려면 몇 가지 중요한 질문을 스스로에게 해야 합니다:
- *데이터가 민감한가요?* 사용자 비밀번호와 같은 민감한 데이터를 클라이언트에 저장하는 것을 피해야 합니다.
- *이 데이터를 얼마나 오래 유지해야 하나요?* 이 데이터를 현재 세션 동안만 액세스할 계획인가요, 아니면 영구적으로 저장하고 싶나요?
- *이 데이터를 얼마나 오래 유지해야 하나요?* 이 데이터를 현재 세션에서만 액세스할 계획인가요, 아니면 영구적으로 저장하고 싶나요?
웹 앱 내부에서 정보를 저장하는 방법은 원하는 결과에 따라 다양합니다. 예를 들어, URL을 사용하여 검색 쿼리를 저장하고 사용자 간에 공유 가능하게 만들 수 있습니다. 또한 [HTTP 쿠키](https://developer.mozilla.org/docs/Web/HTTP/Cookies)를 사용하여 서버와 데이터를 공유해야 하는 경우, 예를 들어 [인증](https://en.wikipedia.org/wiki/Authentication) 정보를 저장할 수 있습니다.
웹 앱 내부에서 정보를 저장하는 방법은 원하는 목표에 따라 다양합니다. 예를 들어, 검색 쿼리를 URL에 저장하여 사용자 간에 공유 가능하게 만들 수 있습니다. 또한 [인증](https://en.wikipedia.org/wiki/Authentication) 정보와 같이 서버와 데이터를 공유해야 하는 경우 [HTTP 쿠키](https://developer.mozilla.org/docs/Web/HTTP/Cookies)를 사용할 수 있습니다.
브라우저 API 중 데이터를 저장하는 데 특히 흥미로운 두 가지 옵션이 있습니다:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): 현재 웹 사이트에 특정한 데이터를 세션 간에 지속적으로 저장할 수 있는 [키/값 저장소](https://en.wikipedia.org/wiki/Key%E2%80%93value_database). 저장된 데이터는 절대 만료되지 않습니다.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): 현재 웹 사이트에 특정한 데이터를 세션 간에 지속적으로 저장할 수 있는 [키/값 저장소](https://en.wikipedia.org/wiki/Key%E2%80%93value_database)입니다. 저장된 데이터는 절대 만료되지 않습니다.
이 두 API는 모두 [문자열](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)만 저장할 수 있다는 점에 유의하세요. 복잡한 객체를 저장하려면 [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)를 사용하여 [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) 형식으로 직렬화해야 합니다.
✅ 서버 없이 작동하는 웹 앱을 만들고 싶다면 [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API)를 사용하여 클라이언트에서 데이터베이스를 생성할 수도 있습니다. 이는 고급 사용 사례나 많은 데이터를 저장해야 할 때 예약되어 있으며, 사용하기 더 복잡합니다.
✅ 서버 없이 작동하는 웹 앱을 만들고 싶다면 [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API)를 사용하여 클라이언트에서 데이터베이스를 생성할 수도 있습니다. 이는 고급 사용 사례나 상당한 양의 데이터를 저장해야 할 때 예약된 옵션으로, 사용하기 더 복잡합니다.
### 작업
사용자가 *로그아웃* 버튼을 명시적으로 클릭할 때까지 로그인 상태를 유지하도록 하기 위해`localStorage`를 사용하여 계정 데이터를 저장할 것입니다. 먼저 데이터를 저장하는 데 사용할 키를 정의하세요.
사용자가 *로그아웃* 버튼을 명시적으로 클릭할 때까지 로그인 상태를 유지하고 싶으므로`localStorage`를 사용하여 계정 데이터를 저장할 것입니다. 먼저 데이터를 저장하는 데 사용할 키를 정의해 보겠습니다.
이로써 사용자 계정 데이터는 중앙 집중화된 상태 업데이트 덕분에 항상 최신 상태로 유지되며 지속적으로 저장됩니다. 이전 리팩토링이 이제부터 빛을 발하기 시작합니다 🙂.
이로써 사용자 계정 데이터는 중앙 집중화된 상태 업데이트 덕분에 항상 최신 상태로 유지됩니다. 이전 리팩토링의 이점을 누리기 시작하는 순간입니다 🙂.
데이터가 저장되었으므로 앱이 로드될 때 데이터를 복원하는 것도 처리해야 합니다. 초기화 코드가 점점 많아지므로 `app.js` 하단의 이전 코드를 포함하는 새로운 `init` 함수를 만드는 것이 좋습니다:
데이터가 저장되었으므로 앱이 로드될 때 이를 복원하는 것도 처리해야 합니다. 초기화 코드가 점점 많아지므로 `app.js` 하단의 이전 코드를 포함하는 새로운 `init` 함수를 만드는 것이 좋습니다:
```js
function init() {
@ -202,9 +202,9 @@ function init() {
init();
```
여기서 저장된 데이터를 복구하고, 데이터가 있으면 상태를 적절히 업데이트합니다. 페이지 업데이트 중 상태에 의존하는 코드가 있을 수 있으므로 *경로를 업데이트하기 전에* 이 작업을 수행하는 것이 중요합니다.
여기서 저장된 데이터를 검색하고, 데이터가 있으면 상태를 적절히 업데이트합니다. 페이지 업데이트 중 상태를 사용하는 코드가 있을 수 있으므로 경로를 업데이트하기 *전에* 이를 수행하는 것이 중요합니다.
이제 *대시보드* 페이지를 애플리케이션 기본 페이지로 만들 수 있습니다. 계정 데이터가 없으면 대시보드가 *로그인* 페이지로 리디렉션합니다. `updateRoute()`에서 기본값 `return navigate('/login');`을 `return navigate('/dashboard');`로 교체하세요.
이제 계정 데이터가 지속되므로 *대시보드* 페이지를 애플리케이션 기본 페이지로 만들 수 있습니다. 데이터가 없으면 대시보드가 *로그인* 페이지로 리디렉션합니다. `updateRoute()`에서 기본값 `return navigate('/login');`을 `return navigate('/dashboard');`로 교체하세요.
이제 앱에 로그인하고 페이지를 새로고침해 보세요. 대시보드에 머물러야 합니다. 이 업데이트로 초기 문제를 모두 해결했습니다...
@ -212,7 +212,7 @@ init();
...하지만 새로운 문제가 생겼을 수도 있습니다. 이런!
`test` 계정으로 대시보드에 이동한 다음 터미널에서 다음 명령을 실행하여 새 거래를 생성하세요:
`test` 계정을 사용하여 대시보드로 이동한 다음 터미널에서 새 거래를 생성하기 위해 다음 명령을 실행하세요:
```sh
curl --request POST \
@ -223,7 +223,7 @@ curl --request POST \
이제 브라우저에서 대시보드 페이지를 새로고침해 보세요. 어떤 일이 발생하나요? 새 거래가 보이나요?
`localStorage` 덕분에 상태가 무기한 지속되지만, 이는 앱에서 로그아웃하고 다시 로그인할 때까지 절대 업데이트되지 않는다는 것을 의미합니다!
`localStorage` 덕분에 상태가 무기한 지속되지만, 이는 앱에서 로그아웃하고 다시 로그인할 때까지 절대 업데이트되지 않는다는 뜻이기도 합니다!
이를 해결하기 위한 한 가지 전략은 대시보드가 로드될 때마다 계정 데이터를 다시 로드하여 오래된 데이터를 방지하는 것입니다.
@ -249,7 +249,7 @@ async function updateAccountData() {
이 메서드는 현재 로그인 상태인지 확인한 다음 서버에서 계정 데이터를 다시 로드합니다.
`refresh`라는 새 함수를 만드세요:
`refresh`라는 또 다른 함수를 만드세요:
```js
async function refresh() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
이 함수는 계정 데이터를 업데이트한 다음 대시보드 페이지의 HTML을 업데이트합니다. 이는 대시보드 경로가 로드될 때 호출해야 하는 함수입니다. 경로 정의를 다음으로 업데이트하세요:
이 함수는 계정 데이터를 업데이트한 다음 대시보드 페이지의 HTML을 업데이트합니다. 대시보드 경로가 로드될 때 호출해야 하는 함수입니다. 경로 정의를 다음으로 업데이트하세요:
```js
const routes = {
@ -275,20 +275,20 @@ const routes = {
이제 대시보드가 로드될 때마다 계정 데이터를 다시 로드하므로, *모든 계정* 데이터를 지속적으로 저장할 필요가 있다고 생각하나요?
팀원들과 함께 작업하여 `localStorage`에 저장하고 로드하는 데이터를 앱이 작동하는 데 절대적으로 필요한 데이터만 포함하도록 변경해 보세요.
팀원들과 함께 `localStorage`에 저장하고 로드하는 데이터를 앱이 작동하는 데 절대적으로 필요한 데이터만 포함하도록 변경해 보세요.
## 강의 후 퀴즈
[강의 후 퀴즈](https://ff-quizzes.netlify.app/web/quiz/48)
## 과제
[트랜잭션 추가] 대화 상자 구현
다음은 과제를 완료한 후의 예시 결과입니다:
["거래 추가" 대화상자 구현하기](assignment.md)
과제를 완료한 후의 예시 결과는 다음과 같습니다:

이 문서는 AI 번역 서비스 [Co-op Translator](https://github.com/Azure/co-op-translator)를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있지만, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서의 원어 버전을 권위 있는 출처로 간주해야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 책임을 지지 않습니다.
이 문서는 AI 번역 서비스 [Co-op Translator](https://github.com/Azure/co-op-translator)를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있으나, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서의 원어 버전이 권위 있는 출처로 간주되어야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다.
[Klausimynas prieš paskaitą](https://ff-quizzes.netlify.app/web/quiz/47)
### Įvadas
Kai internetinė programėlė auga, tampa sudėtinga sekti visus duomenų srautus. Kuris kodas gauna duomenis, kuris puslapis juos naudoja, kur ir kada juos reikia atnaujinti... lengva pasimesti ir sukurti netvarkingą kodą, kurį sunku prižiūrėti. Tai ypač aktualu, kai reikia dalintis duomenimis tarp skirtingų programėlės puslapių, pavyzdžiui, vartotojo duomenimis. *Būsenos valdymo* koncepcija visada egzistavo įvairiose programose, tačiau, augant internetinių programėlių sudėtingumui, tai tapo svarbiu aspektu kuriant programas.
Kai internetinė programėlė auga, tampa sudėtinga sekti visus duomenų srautus. Kuris kodas gauna duomenis, kuris puslapis juos naudoja, kur ir kada jie turi būti atnaujinami... lengva pasimesti tarp netvarkingo kodo, kurį sunku prižiūrėti. Tai ypač aktualu, kai reikia dalintis duomenimis tarp skirtingų programėlės puslapių, pavyzdžiui, vartotojo duomenimis. *Būsenos valdymo* koncepcija visada egzistavo įvairiose programose, tačiau internetinėms programėlėms tampant vis sudėtingesnėms, tai tapo svarbiu aspektu, apie kurį reikia galvoti kuriant.
Šioje paskutinėje dalyje peržiūrėsime sukurtą programėlę, kad iš naujo apgalvotume, kaip valdoma būsena, leisdami palaikyti naršyklės atnaujinimą bet kuriuo metu ir išsaugoti duomenis tarp vartotojo sesijų.
Šioje paskutinėje dalyje peržiūrėsime sukurtą programėlę, kad iš naujo apgalvotume, kaip valdoma būsena, suteikiant galimybę naršyklėje atnaujinti puslapį bet kuriuo metu ir išsaugoti duomenis tarp vartotojo sesijų.
### Privalomos sąlygos
Prieš šią pamoką turite būti baigę [duomenų gavimo](../3-data/README.md) dalį. Taip pat turite įdiegti [Node.js](https://nodejs.org) ir [paleisti serverio API](../api/README.md) lokaliai, kad galėtumėte valdyti paskyros duomenis.
Turite būti baigę [duomenų gavimo](../3-data/README.md) dalį šiai pamokai. Taip pat turite įdiegti [Node.js](https://nodejs.org) ir [paleisti serverio API](../api/README.md) lokaliai, kad galėtumėte valdyti paskyros duomenis.
Galite patikrinti, ar serveris veikia tinkamai, vykdydami šią komandą terminale:
@ -34,9 +34,9 @@ curl http://localhost:5000/api
## Iš naujo apgalvokite būsenos valdymą
[Ankstesnėje pamokoje](../3-data/README.md) pristatėme pagrindinę būsenos koncepciją mūsų programėlėje su globaliu `account` kintamuoju, kuris saugo prisijungusio vartotojo banko duomenis. Tačiau dabartinėje implementacijoje yra keletas trūkumų. Pabandykite atnaujinti puslapį, kai esate prietaisų skydelyje. Kas nutinka?
[Ankstesnėje pamokoje](../3-data/README.md) pristatėme pagrindinę būsenos koncepciją mūsų programėlėje su globaliu `account` kintamuoju, kuriame saugomi šiuo metu prisijungusio vartotojo banko duomenys. Tačiau dabartinis įgyvendinimas turi tam tikrų trūkumų. Pabandykite atnaujinti puslapį, kai esate prietaisų skydelyje. Kas nutinka?
Šiuo metu yra trys problemos:
Dabartiniame kode yra 3 problemos:
- Būsena nėra išsaugoma, nes naršyklės atnaujinimas grąžina jus į prisijungimo puslapį.
- Yra kelios funkcijos, kurios keičia būseną. Kai programėlė auga, tampa sunku sekti pakeitimus, ir lengva pamiršti atnaujinti vieną iš jų.
@ -51,13 +51,13 @@ Galėtume atnaujinti savo kodą, kad išspręstume šias problemas po vieną, ta
- Kaip padaryti, kad duomenų srautai programėlėje būtų suprantami?
- Kaip užtikrinti, kad būsenos duomenys visada būtų sinchronizuoti su vartotojo sąsaja (ir atvirkščiai)?
Kai pasirūpinsite šiomis problemomis, bet kokios kitos problemos, su kuriomis galite susidurti, gali būti jau išspręstos arba tapti lengviau išsprendžiamos. Yra daug galimų būdų šioms problemoms spręsti, tačiau mes pasirinkime bendrą sprendimą, kuris apima **duomenų ir jų keitimo būdų centralizavimą**. Duomenų srautai atrodytų taip:
Kai pasirūpinsite šiomis problemomis, bet kokios kitos problemos, su kuriomis galite susidurti, gali būti jau išspręstos arba tapti lengviau išsprendžiamos. Yra daug galimų būdų šioms problemoms spręsti, tačiau mes pasirinkime bendrą sprendimą, kuris susideda iš **duomenų ir jų keitimo būdų centralizavimo**. Duomenų srautai atrodytų taip:

> Čia neaptarsime dalies, kurioje duomenys automatiškai atnaujina vaizdą, nes tai susiję su pažangesnėmis [reaktyviosios programavimo](https://en.wikipedia.org/wiki/Reactive_programming) koncepcijomis. Tai gera tema gilintis, jei norite išsamiau suprasti.
> Čia neaptarsime dalies, kurioje duomenys automatiškai atnaujina vaizdą, nes tai susiję su pažangesnėmis [reaktyviosios programavimo](https://en.wikipedia.org/wiki/Reactive_programming) koncepcijomis. Tai gera tema gilintis, jei norite daugiau sužinoti.
✅ Yra daug bibliotekų su skirtingais būsenos valdymo metodais, [Redux](https://redux.js.org) yra populiarus pasirinkimas. Susipažinkite su naudojamomis koncepcijomis ir šablonais, nes tai dažnai yra geras būdas sužinoti, su kokiomis problemomis galite susidurti didelėse internetinėse programėlėse ir kaip jas galima išspręsti.
✅ Yra daug bibliotekų su skirtingais būsenos valdymo metodais, [Redux](https://redux.js.org) yra populiarus pasirinkimas. Susipažinkite su naudojamomis koncepcijomis ir šablonais, nes tai dažnai yra geras būdas sužinoti, su kokiomis potencialiomis problemomis galite susidurti didelėse internetinėse programėlėse ir kaip jas galima išspręsti.
### Užduotis
@ -75,7 +75,7 @@ let state = {
};
```
Idėja yra *centralizuoti* visus mūsų programėlės duomenis viename būsenos objekte. Šiuo metu būsenoje turime tik `account`, todėl tai daug nepakeičia, tačiau tai sukuria pagrindą ateities pakeitimams.
Idėja yra *centralizuoti* visus mūsų programėlės duomenis viename būsenos objekte. Šiuo metu būsenoje turime tik `account`, todėl tai daug nepakeičia, tačiau tai sukuria pagrindą ateities pokyčiams.
Taip pat turime atnaujinti funkcijas, kurios jį naudoja. `register()` ir `login()` funkcijose pakeiskite `account = ...` į `state.account = ...`;
@ -85,17 +85,17 @@ Taip pat turime atnaujinti funkcijas, kurios jį naudoja. `register()` ir `login
const account = state.account;
```
Šis pertvarkymas pats savaime neatnešė daug patobulinimų, tačiau idėja buvo sukurti pagrindą kitiems pakeitimams.
Šis pertvarkymas pats savaime neatnešė daug patobulinimų, tačiau idėja buvo sukurti pagrindą tolesniems pakeitimams.
## Sekite duomenų pakeitimus
## Sekite duomenų pokyčius
Dabar, kai sukūrėme `state` objektą duomenims saugoti, kitas žingsnis yra centralizuoti atnaujinimus. Tikslas yra palengvinti bet kokių pakeitimų sekimą ir supratimą, kada jie įvyksta.
Dabar, kai sukūrėme `state` objektą duomenims saugoti, kitas žingsnis yra centralizuoti atnaujinimus. Tikslas yra palengvinti bet kokių pokyčių sekimą ir supratimą, kada jie įvyksta.
Kad būtų išvengta pakeitimų `state` objekte, taip pat yra gera praktika laikyti jį [*nekintamu*](https://en.wikipedia.org/wiki/Immutable_object), tai reiškia, kad jo negalima visiškai modifikuoti. Tai taip pat reiškia, kad jei norite ką nors pakeisti, turite sukurti naują būsenos objektą. Taip apsisaugosite nuo galimų nepageidaujamų [šalutinių poveikių](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) ir atversite galimybes naujoms funkcijoms, tokioms kaip atšaukimo/atstatymo įgyvendinimas, taip pat palengvinsite derinimą. Pavyzdžiui, galėtumėte registruoti kiekvieną būsenos pakeitimą ir išsaugoti jų istoriją, kad suprastumėte klaidos šaltinį.
Kad būtų išvengta pokyčių `state` objekte, taip pat yra gera praktika laikyti jį [*nepakeičiamu*](https://en.wikipedia.org/wiki/Immutable_object), tai reiškia, kad jo negalima visiškai modifikuoti. Tai taip pat reiškia, kad jei norite ką nors pakeisti, turite sukurti naują būsenos objektą. Tokiu būdu apsisaugosite nuo galimų nepageidaujamų [šalutinių poveikių](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) ir atversite galimybes naujoms funkcijoms, tokioms kaip atšaukimo/atstatymo įgyvendinimas, taip pat palengvinsite derinimą. Pavyzdžiui, galėtumėte registruoti kiekvieną būsenos pakeitimą ir išlaikyti jų istoriją, kad suprastumėte klaidos šaltinį.
JavaScript'e galite naudoti [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze), kad sukurtumėte nekintamą objekto versiją. Jei bandysite pakeisti nekintamą objektą, bus išmesta išimtis.
JavaScript'e galite naudoti [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze), kad sukurtumėte nepakeičiamą objekto versiją. Jei bandysite pakeisti nepakeičiamą objektą, bus išmesta išimtis.
✅ Ar žinote skirtumą tarp *paviršutiniškai* ir *giliai* nekintamo objekto? Apie tai galite paskaityti [čia](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Ar žinote skirtumą tarp *paviršutiniškai* ir *giliai* nepakeičiamo objekto? Galite apie tai paskaityti [čia](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Užduotis
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
Šioje funkcijoje sukuriame naują būsenos objektą ir kopijuojame duomenis iš ankstesnės būsenos naudodami [*sklaidos operatorių (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Tada perrašome tam tikrą būsenos objekto savybę naujais duomenimis naudodami [skliaustelių notaciją](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]`. Galiausiai užrakiname objektą, kad išvengtume pakeitimų, naudodami `Object.freeze()`. Šiuo metu būsenoje saugome tik `account`, tačiau naudodami šį metodą galime pridėti tiek savybių, kiek reikia.
Šioje funkcijoje sukuriame naują būsenos objektą ir kopijuojame duomenis iš ankstesnės būsenos naudodami [*sklaidos operatorių (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Tada perrašome tam tikrą būsenos objekto savybę naujais duomenimis naudodami [skliaustelių notaciją](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` priskyrimui. Galiausiai užrakiname objektą, kad išvengtume modifikacijų, naudodami `Object.freeze()`. Šiuo metu būsenoje saugome tik `account` savybę, tačiau naudodami šį metodą galite pridėti tiek savybių, kiek reikia.
Taip pat atnaujinsime `state` inicializaciją, kad įsitikintume, jog pradinė būsena taip pat yra užšaldyta:
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
Po to atnaujinkite `register` funkciją, pakeisdami `state.account = result;` į:
Po to atnaujinkite `register` funkciją, pakeisdami `state.account = result;`priskyrimą į:
```js
updateState('account', result);
@ -143,35 +143,35 @@ function logout() {
}
```
`updateDashboard()` funkcijoje pakeiskite peradresavimą `return navigate('/login');` į `return logout();`
`updateDashboard()` funkcijoje pakeiskite peradresavimą `return navigate('/login');` į `return logout()`;
Pabandykite užregistruoti naują paskyrą, atsijungti ir vėl prisijungti, kad patikrintumėte, ar viskas veikia tinkamai.
Pabandykite užregistruoti naują paskyrą, atsijungti ir vėl prisijungti, kad patikrintumėte, ar viskas vis dar veikia tinkamai.
> Patarimas: galite peržiūrėti visus būsenos pakeitimus, pridėdami `console.log(state)``updateState()` funkcijos apačioje ir atidarę naršyklės kūrimo įrankių konsolę.
> Patarimas: galite peržiūrėti visus būsenos pokyčius, pridėdami `console.log(state)``updateState()` apačioje ir atidarę naršyklės kūrimo įrankių konsolę.
## Išsaugokite būseną
Dauguma internetinių programėlių turi išsaugoti duomenis, kad galėtų tinkamai veikti. Visi svarbūs duomenys paprastai saugomi duomenų bazėje ir pasiekiami per serverio API, kaip mūsų atveju vartotojo paskyros duomenys. Tačiau kartais taip pat naudinga išsaugoti kai kuriuos duomenis kliento programėlėje, kuri veikia jūsų naršyklėje, siekiant geresnės vartotojo patirties arba pagerinti įkėlimo našumą.
Daugumai internetinių programėlių reikia išsaugoti duomenis, kad jos galėtų tinkamai veikti. Visi svarbūs duomenys paprastai saugomi duomenų bazėje ir pasiekiami per serverio API, kaip mūsų atveju vartotojo paskyros duomenys. Tačiau kartais taip pat naudinga išsaugoti kai kuriuos duomenis kliento programėlėje, kuri veikia jūsų naršyklėje, siekiant geresnės vartotojo patirties arba pagerinti įkėlimo našumą.
Kai norite išsaugoti duomenis naršyklėje, turėtumėte užduoti sau keletą svarbių klausimų:
- *Ar duomenys yra jautrūs?* Turėtumėte vengti saugoti bet kokius jautrius duomenis kliente, pavyzdžiui, vartotojo slaptažodžius.
- *Kiek laiko jums reikia išlaikyti šiuos duomenis?* Ar planuojate naudoti šiuos duomenis tik dabartinei sesijai, ar norite, kad jie būtų saugomi visam laikui?
- *Kiek laiko jums reikia išlaikyti šiuos duomenis?* Ar planuojate pasiekti šiuos duomenis tik dabartinei sesijai, ar norite, kad jie būtų saugomi visam laikui?
Yra keli būdai saugoti informaciją internetinėje programėlėje, priklausomai nuo to, ką norite pasiekti. Pavyzdžiui, galite naudoti URL, kad saugotumėte paieškos užklausą ir padarytumėte ją dalinamą tarp vartotojų. Taip pat galite naudoti [HTTP slapukus](https://developer.mozilla.org/docs/Web/HTTP/Cookies), jei duomenys turi būti bendrinami su serveriu, pavyzdžiui, [autentifikacijos](https://en.wikipedia.org/wiki/Authentication) informacija.
Yra keli būdai saugoti informaciją internetinėje programėlėje, priklausomai nuo to, ką norite pasiekti. Pavyzdžiui, galite naudoti URL, kad saugotumėte paieškos užklausą ir padarytumėte ją dalinamą tarp vartotojų. Taip pat galite naudoti [HTTP slapukus](https://developer.mozilla.org/docs/Web/HTTP/Cookies), jei duomenys turi būti dalinami su serveriu, pavyzdžiui, [autentifikacijos](https://en.wikipedia.org/wiki/Authentication) informacija.
Kita galimybė yra naudoti vieną iš daugelio naršyklės API duomenims saugoti. Dvi iš jų yra ypač įdomios:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Raktų/Verčių saugykla](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), leidžianti išsaugoti duomenis, susijusius su dabartine svetaine, tarp skirtingų sesijų. Išsaugoti duomenys niekada neišnyksta.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): veikia taip pat kaip `localStorage`, išskyrus tai, kad saugomi duomenys ištrinami, kai sesija baigiasi (kai naršyklė uždaroma).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Raktų/Verčių saugykla](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), leidžianti išsaugoti duomenis, specifinius dabartinei svetainei, tarp skirtingų sesijų. Joje saugomi duomenys niekada nesibaigia.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ši veikia taip pat kaip `localStorage`, išskyrus tai, kad joje saugomi duomenys ištrinami, kai sesija baigiasi (kai naršyklė uždaroma).
Atkreipkite dėmesį, kad abi šios API leidžia saugoti tik [eilutes](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Jei norite saugoti sudėtingus objektus, turėsite juos serializuoti į [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) formatą, naudodami [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Jei norite sukurti internetinę programėlę, kuri neveikia su serveriu, taip pat galima sukurti duomenų bazę kliente, naudojant [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ši galimybė skirta pažangesniems atvejams arba jei reikia saugoti didelį duomenų kiekį, nes ją naudoti yra sudėtingiau.
✅ Jei norite sukurti internetinę programėlę, kuri neveikia su serveriu, taip pat galima sukurti duomenų bazę kliente, naudojant [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ši API skirta pažangesniems atvejams arba jei reikia saugoti didelį duomenų kiekį, nes ją naudoti yra sudėtingiau.
### Užduotis
Norime, kad mūsų vartotojai liktų prisijungę, kol jie aiškiai nepaspaudžia*Atsijungti* mygtuko, todėl naudosime `localStorage`, kad išsaugotume paskyros duomenis. Pirmiausia apibrėžkime raktą, kurį naudosime duomenims saugoti.
Norime, kad mūsų vartotojai liktų prisijungę, kol jie aiškiai nepaspaus*Atsijungti* mygtuko, todėl naudosime `localStorage`, kad saugotume paskyros duomenis. Pirmiausia apibrėžkime raktą, kurį naudosime duomenims saugoti.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Tada pridėkite šią eilutę `updateState()` funkcijos pabaigoje:
Taip vartotojo paskyros duomenys bus išsaugoti ir visada atnaujinami, nes anksčiau centralizavome visus būsenos atnaujinimus. Štai kur pradedame naudotis visais ankstesniais pertvarkymais 🙂.
Su tuo vartotojo paskyros duomenys bus išsaugoti ir visada atnaujinami, nes anksčiau centralizavome visus būsenos atnaujinimus. Čia pradedame naudotis visais ankstesniais pertvarkymais 🙂.
Kadangi duomenys yra išsaugoti, taip pat turime pasirūpinti jų atkūrimu, kai programėlė įkeliama. Kadangi pradėsime turėti daugiau inicializacijos kodo, gali būti gera idėja sukurti naują `init` funkciją, kuri taip pat apimtų ankstesnį kodą `app.js` apačioje:
Kadangi duomenys yra išsaugoti, taip pat turime pasirūpinti jų atkūrimu, kai programėlė įkeliama. Kadangi pradėsime turėti daugiau inicializacijos kodo, gali būti gera idėja sukurti naują `init` funkciją, kuri taip pat apimtų mūsų ankstesnį kodą `app.js` apačioje:
```js
function init() {
@ -223,9 +223,9 @@ curl --request POST \
Dabar pabandykite atnaujinti prietaisų skydelio puslapį naršyklėje. Kas nutinka? Ar matote naują operaciją?
Būsena yra išsaugota neribotam laikui dėl `localStorage`, tačiau tai taip pat reiškia, kad ji niekada neatnaujinama, kol neišsijungiate iš programėlės ir vėl neprisijungiate!
Būsena yra išsaugota neribotam laikui dėl `localStorage`, tačiau tai taip pat reiškia, kad ji niekada nėra atnaujinama, kol neišsijungiate iš programėlės ir vėl neprisijungiate!
Vienas galimas sprendimas yra atnaujinti paskyros duomenis kiekvieną kartą, kai įkeliama prietaisų skydelio puslapis, kad būtų išvengta pasenusių duomenų.
Vienas galimas strategijos būdas tai išspręsti yra atnaujinti paskyros duomenis kiekvieną kartą, kai įkeliama prietaisų skydelio puslapis, kad būtų išvengta pasenusių duomenų.
### Užduotis
@ -258,23 +258,18 @@ async function refresh() {
}
```
Ši funkcija atnaujina paskyros duomenis, tada pasirūpina prietaisų skydelio puslapio HTML atnaujinimu. Tai yra tai, ką turime iškviesti, kai įkeliama prietaisų skydelio maršrutas. Atnaujinkite maršruto apibrėžimą su:
Ši funkcija atnaujina paskyros duomenis, tada pasirūpina prietaisų skydelio puslapio HTML atnaujinimu. Tai yra tai, ką turime iškviesti, kai įkeli
Šis dokumentas buvo išverstas naudojant AI vertimo paslaugą [Co-op Translator](https://github.com/Azure/co-op-translator). Nors siekiame tikslumo, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba turėtų būti laikomas autoritetingu šaltiniu. Kritinei informacijai rekomenduojama profesionali žmogaus vertimo paslauga. Mes neprisiimame atsakomybės už nesusipratimus ar klaidingus interpretavimus, atsiradusius naudojant šį vertimą.
**Atsakomybės atsisakymas**:
Šis dokumentas buvo išverstas naudojant AI vertimo paslaugą [Co-op Translator](https://github.com/Azure/co-op-translator). Nors siekiame tikslumo, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba turėtų būti laikomas autoritetingu šaltiniu. Dėl svarbios informacijos rekomenduojama profesionali žmogaus vertimo paslauga. Mes neprisiimame atsakomybės už nesusipratimus ar neteisingus aiškinimus, kylančius dėl šio vertimo naudojimo.
वेब अॅप्लिकेशन मोठे होत असताना, डेटा फ्लो ट्रॅक करणे आव्हानात्मक होते. कोणता कोड डेटा मिळवतो, कोणते पृष्ठ त्याचा वापर करते, तो कधी आणि कुठे अपडेट करायचा... हे सर्व व्यवस्थित ठेवणे कठीण होऊ शकते. विशेषतः जेव्हा तुम्हाला तुमच्या अॅपच्या वेगवेगळ्या पृष्ठांमध्ये डेटा शेअर करायचा असतो, जसे की वापरकर्त्याचा डेटा. *स्टेट मॅनेजमेंट* ही संकल्पना सर्व प्रकारच्या प्रोग्राम्समध्ये नेहमीच अस्तित्वात असते, परंतु वेब अॅप्स अधिक जटिल होत असल्याने विकासादरम्यान यावर विचार करणे महत्त्वाचे ठरते.
जसे जसे वेब अॅप्लिकेशन मोठे होते, तसतसे सर्व डेटा प्रवाहांचा मागोवा ठेवणे कठीण होते. कोणता कोड डेटा मिळवतो, कोणते पृष्ठ तो वापरते, तो कधी आणि कुठे अद्यतनित करायचा आहे... हे सर्व लक्षात ठेवणे कठीण होऊ शकते आणि त्यामुळे कोड गोंधळलेला आणि देखभाल करणे कठीण होतो. हे विशेषतः खरे आहे जेव्हा तुम्हाला तुमच्या अॅपच्या वेगवेगळ्या पृष्ठांमध्ये डेटा सामायिक करायचा असतो, जसे की वापरकर्त्याचा डेटा. *स्टेट मॅनेजमेंट* ही संकल्पना सर्व प्रकारच्या प्रोग्राम्समध्ये नेहमीच अस्तित्वात होती, परंतु वेब अॅप्सची जटिलता वाढत असल्याने विकासादरम्यान याचा विचार करणे महत्त्वाचे ठरते.
या अंतिम भागात, आपण तयार केलेल्या अॅपवर पुनर्विचार करू आणि स्टेट कसे व्यवस्थापित केले जाते ते पाहू, ब्राउझर रिफ्रेशला कोणत्याही वेळी समर्थन देणे आणि वापरकर्ता सत्रांमध्ये डेटा टिकवून ठेवणे यासाठी.
या अंतिम भागात, आपण तयार केलेल्या अॅपवर पुनर्विचार करू आणि स्टेट कसे व्यवस्थापित करायचे यावर लक्ष केंद्रित करू, ज्यामुळे कोणत्याही क्षणी ब्राउझर रीफ्रेशला समर्थन मिळेल आणि वापरकर्ता सत्रांमध्ये डेटा टिकवून ठेवता येईल.
### पूर्वतयारी
### पूर्वअट
या धड्यासाठी तुम्ही वेब अॅपचा [डेटा फेचिंग](../3-data/README.md) भाग पूर्ण केला असावा. तुम्हाला [Node.js](https://nodejs.org) स्थापित करणे आणि [सर्व्हर API चालवणे](../api/README.md) स्थानिक पातळीवर आवश्यक आहे जेणेकरून तुम्ही खाते डेटा व्यवस्थापित करू शकता.
या धड्यासाठी तुम्ही [डेटा फेचिंग](../3-data/README.md) भाग पूर्ण केलेला असणे आवश्यक आहे. तुम्हाला [Node.js](https://nodejs.org) स्थापित करणे आणि [सर्व्हर API](../api/README.md) स्थानिकरित्या चालवणे आवश्यक आहे जेणेकरून तुम्ही खाते डेटा व्यवस्थापित करू शकाल.
सर्व्हर योग्यरित्या चालू आहे का हे तपासण्यासाठी तुम्ही टर्मिनलमध्ये हा आदेश चालवू शकता:
तुम्ही टर्मिनलमध्ये हा आदेश चालवून सर्व्हर योग्यरित्या चालू आहे का ते तपासू शकता:
```sh
curl http://localhost:5000/api
@ -34,40 +34,40 @@ curl http://localhost:5000/api
## स्टेट मॅनेजमेंटवर पुनर्विचार करा
[मागील धडा](../3-data/README.md) मध्ये, आम्ही `account` नावाच्या ग्लोबल व्हेरिएबलसह अॅपमध्ये स्टेटची मूलभूत संकल्पना सादर केली होती, ज्यामध्ये सध्या लॉग इन केलेल्या वापरकर्त्याचा बँक डेटा आहे. परंतु, आमच्या सध्याच्या अंमलबजावणीमध्ये काही त्रुटी आहेत. डॅशबोर्डवर असताना पृष्ठ रिफ्रेश करण्याचा प्रयत्न करा. काय होते?
[मागील धड्यात](../3-data/README.md), आम्ही `account` नावाच्या ग्लोबल व्हेरिएबलसह अॅपमध्ये स्टेटची मूलभूत संकल्पना सादर केली, ज्यामध्ये सध्या लॉग इन केलेल्या वापरकर्त्याचा बँक डेटा आहे. तथापि, आमच्या सध्याच्या अंमलबजावणीत काही त्रुटी आहेत. डॅशबोर्डवर असताना पृष्ठ रीफ्रेश करण्याचा प्रयत्न करा. काय होते?
सध्याच्या कोडमध्ये 3 समस्या आहेत:
- स्टेट टिकवून ठेवले जात नाही, कारण ब्राउझर रिफ्रेश तुम्हाला लॉगिन पृष्ठावर परत नेते.
- स्टेट बदलण्यासाठी अनेक फंक्शन्स आहेत. अॅप मोठा होत असताना, बदल ट्रॅक करणे कठीण होऊ शकते आणि एखादा अपडेट विसरणे सोपे होते.
- स्टेट साफ केली जात नाही, त्यामुळे तुम्ही *Logout* क्लिक केल्यावरही लॉगिन पृष्ठावर असताना खाते डेटा तिथेच राहतो.
- स्टेट टिकवून ठेवली जात नाही, कारण ब्राउझर रीफ्रेश केल्यावर तुम्हाला लॉगिन पृष्ठावर परत नेले जाते.
- स्टेट बदलण्यासाठी अनेक फंक्शन्स आहेत. अॅप मोठे होत असताना, बदलांचा मागोवा ठेवणे कठीण होऊ शकते आणि एखाद्या अद्यतनाची आठवण राहू शकते.
- स्टेट साफ केली जात नाही, त्यामुळे जेव्हा तुम्ही *लॉगआउट* क्लिक करता, तेव्हा लॉगिन पृष्ठावर असतानाही खाते डेटा तिथेच राहतो.
आम्ही या समस्यांवर एक-एक करून उपाय करण्यासाठी आमचा कोड अपडेट करू शकतो, परंतु यामुळे कोड डुप्लिकेशन वाढेल आणि अॅप अधिक जटिल आणि देखभाल करणे कठीण होईल. किंवा आम्ही काही मिनिटे थांबून आमची रणनीती पुन्हा विचार करू शकतो.
आपण या समस्यांवर एक एक करून उपाय करण्यासाठी कोड अद्यतनित करू शकतो, परंतु यामुळे कोड डुप्लिकेशन वाढेल आणि अॅप अधिक जटिल आणि देखभाल करणे कठीण होईल. किंवा आपण काही मिनिटे थांबून आपल्या रणनीतीचा पुनर्विचार करू शकतो.
> आम्ही येथे कोणत्या समस्या सोडवण्याचा प्रयत्न करत आहोत?
> येथे आपण खरोखर कोणत्या समस्यांचे निराकरण करण्याचा प्रयत्न करत आहोत?
[स्टेट मॅनेजमेंट](https://en.wikipedia.org/wiki/State_management) म्हणजे या दोन विशिष्ट समस्यांचे चांगले समाधान शोधणे:
[स्टेट मॅनेजमेंट](https://en.wikipedia.org/wiki/State_management) म्हणजे या दोन विशिष्ट समस्यांचे निराकरण करण्यासाठी चांगला दृष्टिकोन शोधणे:
- अॅपमधील डेटा फ्लो समजण्यासारखा कसा ठेवायचा?
- अॅपमधील डेटा प्रवाह समजण्यासारखा कसा ठेवायचा?
- स्टेट डेटा नेहमी वापरकर्ता इंटरफेससह (आणि उलट) समक्रमित कसा ठेवायचा?
एकदा तुम्ही याची काळजी घेतली की, तुम्हाला असलेल्या इतर कोणत्याही समस्या आधीच सोडवल्या जाऊ शकतात किंवा सोडवणे सोपे झाले आहे. या समस्यांचे निराकरण करण्यासाठी अनेक संभाव्य दृष्टिकोन आहेत, परंतु आम्ही **डेटा आणि त्यात बदल करण्याचे मार्ग केंद्रीकृत करण्याचे** सामान्य समाधान निवडू. डेटा फ्लो असे असेल:
एकदा तुम्ही यांची काळजी घेतली की, तुम्हाला असलेल्या इतर कोणत्याही समस्या कदाचित आधीच निराकरण झाल्या असतील किंवा त्या सोडवणे सोपे झाले असेल. या समस्यांचे निराकरण करण्यासाठी अनेक दृष्टिकोन आहेत, परंतु आम्ही **डेटा आणि तो बदलण्याच्या पद्धती केंद्रीकृत करण्याचा** एक सामान्य उपाय निवडू. डेटा प्रवाह असा असेल:


> आम्ही येथे डेटा स्वयंचलितपणे दृश्य अपडेट ट्रिगर करण्याचा भाग कव्हर करणार नाही, कारण ते [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming) च्या अधिक प्रगत संकल्पनांशी संबंधित आहे. जर तुम्हाला सखोल अभ्यास करायचा असेल तर हा एक चांगला विषय आहे.
> येथे डेटा स्वयंचलितपणे दृश्य अद्यतनित करतो तो भाग आम्ही कव्हर करणार नाही, कारण तो [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming) च्या अधिक प्रगत संकल्पनांशी संबंधित आहे. जर तुम्हाला सखोल अभ्यास करायचा असेल तर हा एक चांगला पुढील विषय आहे.
✅ स्टेट मॅनेजमेंटसाठी वेगवेगळ्या दृष्टिकोनांसह अनेक लायब्ररी उपलब्ध आहेत, [Redux](https://redux.js.org) हे एक लोकप्रिय पर्याय आहे. मोठ्या वेब अॅप्समध्ये तुम्हाला कोणत्या संभाव्य समस्या येऊ शकतात आणि त्या कशा सोडवता येतील हे शिकण्यासाठी वापरले जाणारे संकल्पना आणि पॅटर्न पाहा.
✅ स्टेट मॅनेजमेंटसाठी वेगवेगळ्या दृष्टिकोनांसह अनेक लायब्ररी उपलब्ध आहेत, [Redux](https://redux.js.org) हा एक लोकप्रिय पर्याय आहे. मोठ्या वेब अॅप्समध्ये तुम्ही कोणत्या संभाव्य समस्यांना सामोरे जाऊ शकता आणि त्या कशा सोडवता येतील हे शिकण्यासाठी वापरले जाणारे संकल्पना आणि पॅटर्न पाहा.
### कार्य
आम्ही थोडे रिफॅक्टरिंग करून सुरुवात करू. `account` डिक्लरेशन बदला:
आम्ही थोडेसे कोड पुनर्रचना करून सुरुवात करू. `account` डिक्लेरेशन बदला:
```js
let account = null;
```
सह:
यासह:
```js
let state = {
@ -75,9 +75,9 @@ let state = {
};
```
आमच्या अॅपमधील सर्व डेटा एका सिंगल स्टेट ऑब्जेक्टमध्ये *केंद्रीकृत* करण्याची कल्पना आहे. सध्या स्टेटमध्ये फक्त `account` आहे त्यामुळे फारसा बदल होत नाही, परंतु यामुळे भविष्यातील सुधारणा शक्य होतात.
कल्पना म्हणजे सर्व अॅप डेटा एका सिंगल स्टेट ऑब्जेक्टमध्ये *केंद्रीकृत* करणे. सध्या स्टेटमध्ये फक्त `account` आहे त्यामुळे फारसा फरक पडत नाही, परंतु भविष्यातील बदलांसाठी मार्ग तयार होतो.
आम्हाला ते वापरणाऱ्या फंक्शन्स देखील अपडेट कराव्या लागतील. `register()` आणि `login()` फंक्शन्समध्ये, `account = ...`बदला `state.account = ...`;
आपल्याला ते वापरणाऱ्या फंक्शन्स देखील अद्यतनित कराव्या लागतील. `register()` आणि `login()` फंक्शन्समध्ये, `account = ...` ला `state.account = ...` ने बदला;
`updateDashboard()` फंक्शनच्या शीर्षस्थानी, ही ओळ जोडा:
@ -85,17 +85,17 @@ let state = {
const account = state.account;
```
या रिफॅक्टरिंगमुळे फारसे सुधारणा झाल्या नाहीत, परंतु पुढील बदलांसाठी पाया घालणे हे उद्दिष्ट होते.
या पुनर्रचनेने स्वतःहून फारसे सुधारणा केल्या नाहीत, परंतु पुढील बदलांसाठी पाया घालण्याचा विचार होता.
## डेटा बदल ट्रॅक करा
## डेटा बदलांचा मागोवा घ्या
आता आपण डेटा स्टोअर करण्यासाठी `state` ऑब्जेक्ट तयार केला आहे, पुढील पायरी म्हणजे अपडेट्स केंद्रीकृत करणे. कोणतेही बदल आणि ते कधी घडतात हे ट्रॅक करणे सोपे बनवणे हे उद्दिष्ट आहे.
आता आपण डेटा साठवण्यासाठी `state` ऑब्जेक्ट तयार केला आहे, पुढील पाऊल म्हणजे अद्यतनांना केंद्रीकृत करणे. उद्दिष्ट म्हणजे कोणतेही बदल आणि ते कधी घडतात याचा मागोवा घेणे सोपे करणे.
`state` ऑब्जेक्टमध्ये बदल होऊ नयेत यासाठी, त्याला [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) मानणे चांगली पद्धत आहे, म्हणजे त्यात अजिबात बदल करता येणार नाही. याचा अर्थ तुम्हाला त्यात काहीही बदल करायचे असल्यास नवीन स्टेट ऑब्जेक्ट तयार करावा लागेल. असे करून, तुम्ही संभाव्य अवांछित [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) पासून संरक्षण तयार करता आणि तुमच्या अॅपमध्ये नवीन वैशिष्ट्ये लागू करण्यासाठी शक्यता उघडता, जसे की undo/redo लागू करणे, तसेच डिबग करणे सोपे बनवता. उदाहरणार्थ, तुम्ही स्टेटमध्ये केलेला प्रत्येक बदल लॉग करू शकता आणि बगचा स्रोत समजण्यासाठी बदलांचा इतिहास ठेवू शकता.
`state` ऑब्जेक्टमध्ये बदल होऊ नयेत यासाठी, त्याला [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) मानणे चांगली पद्धत आहे, म्हणजे त्यात अजिबात बदल करता येणार नाही. याचा अर्थ असा की तुम्हाला त्यात काहीही बदल करायचे असल्यास नवीन स्टेट ऑब्जेक्ट तयार करावा लागेल. असे केल्याने, तुम्ही संभाव्य अवांछित [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) पासून संरक्षण तयार करता आणि तुमच्या अॅपमध्ये नवीन वैशिष्ट्ये अंमलात आणण्याच्या शक्यता उघडता, जसे की undo/redo अंमलात आणणे, तसेच डिबग करणे सोपे होते. उदाहरणार्थ, तुम्ही स्टेटमध्ये केलेला प्रत्येक बदल लॉग करू शकता आणि बगचा स्रोत समजण्यासाठी बदलांचा इतिहास ठेवू शकता.
JavaScript मध्ये, [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) वापरून ऑब्जेक्टची immutable आवृत्ती तयार करू शकता. जर तुम्ही immutable ऑब्जेक्टमध्ये बदल करण्याचा प्रयत्न केला तर अपवाद निर्माण होईल.
JavaScript मध्ये, [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) वापरून ऑब्जेक्टची immutable आवृत्ती तयार करू शकता. जर तुम्ही immutable ऑब्जेक्टमध्ये बदल करण्याचा प्रयत्न केला, तर अपवाद निर्माण होईल.
✅ *shallow* आणि *deep* immutable ऑब्जेक्टमधील फरक तुम्हाला माहित आहे का? तुम्ही याबद्दल [इथे](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) वाचू शकता.
✅ तुम्हाला *shallow* आणि *deep* immutable ऑब्जेक्टमधील फरक माहित आहे का? तुम्ही याबद्दल [येथे](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) वाचू शकता.
### कार्य
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
या फंक्शनमध्ये, आम्ही नवीन स्टेट ऑब्जेक्ट तयार करत आहोत आणि [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) वापरून मागील स्टेटमधून डेटा कॉपी करत आहोत. नंतर आम्ही [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` वापरून स्टेट ऑब्जेक्टच्या विशिष्ट प्रॉपर्टीला नवीन डेटासह ओव्हरराइड करतो. शेवटी, आम्ही `Object.freeze()` वापरून ऑब्जेक्ट लॉक करतो जेणेकरून त्यात बदल होऊ नयेत. सध्या स्टेटमध्ये फक्त `account` प्रॉपर्टी आहे, परंतु या दृष्टिकोनाने तुम्ही स्टेटमध्ये जितक्या प्रॉपर्टी आवश्यक आहेत तितक्या जोडू शकता.
या फंक्शनमध्ये, आम्ही नवीन स्टेट ऑब्जेक्ट तयार करतो आणि [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) वापरून मागील स्टेटमधून डेटा कॉपी करतो. त्यानंतर आम्ही [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` वापरून स्टेट ऑब्जेक्टच्या विशिष्ट प्रॉपर्टीला नवीन डेटासह ओव्हरराइड करतो. शेवटी, `Object.freeze()` वापरून ऑब्जेक्ट लॉक करतो. सध्या स्टेटमध्ये फक्त `account` प्रॉपर्टी आहे, परंतु या दृष्टिकोनाने तुम्ही स्टेटमध्ये आवश्यक तेवढ्या प्रॉपर्टी जोडू शकता.
आम्ही `state` इनिशियलायझेशन देखील अपडेट करू जेणेकरून प्रारंभिक स्टेट देखील फ्रोझन असेल:
`state` प्रारंभिकरण देखील अद्यतनित करू जेणेकरून प्रारंभिक स्टेट देखील फ्रीज केले जाईल:
`updateDashboard()` मध्ये, `return navigate('/login');`पुनर्निर्देशन बदलून `return logout();` करा;
नवीन खाते नोंदणी करा, लॉगआउट करा आणि पुन्हा लॉगिन करा आणि सर्व काही योग्य प्रकारे कार्य करत आहे का ते तपासा.
नवीन खाते नोंदणी करा, लॉगआउट करा आणि पुन्हा लॉगिन करून सर्व काही योग्यरित्या कार्यरत आहे का ते तपासा.
> टिप: तुम्ही `updateState()` च्या तळाशी `console.log(state)` जोडून आणि तुमच्या ब्राउझरच्या डेव्हलपमेंट टूल्समध्ये कन्सोल उघडून सर्व स्टेट बदल पाहू शकता.
> टीप: तुम्ही `updateState()` च्या तळाशी `console.log(state)` जोडून आणि तुमच्या ब्राउझरच्या विकास साधनांमध्ये कन्सोल उघडून सर्व स्टेट बदल पाहू शकता.
## स्टेट टिकवून ठेवा
बहुतेक वेब अॅप्सना योग्य प्रकारे कार्य करण्यासाठी डेटा टिकवून ठेवणे आवश्यक असते. सर्व महत्त्वाचा डेटा सामान्यतः डेटाबेसमध्ये संग्रहित केला जातो आणि सर्व्हर API द्वारे प्रवेश केला जातो, जसे की आमच्या बाबतीत वापरकर्ता खाते डेटा. परंतु कधीकधी, ब्राउझरमध्ये चालणाऱ्या क्लायंट अॅपवर काही डेटा टिकवून ठेवणे देखील मनोरंजक असते, चांगल्या वापरकर्ता अनुभवासाठी किंवा लोडिंग कार्यक्षमतेत सुधारणा करण्यासाठी.
बहुतेक वेब अॅप्सना योग्यरित्या कार्य करण्यासाठी डेटा टिकवून ठेवण्याची आवश्यकता असते. सर्व महत्त्वाचा डेटा सहसा डेटाबेसमध्ये संग्रहित केला जातो आणि सर्व्हर API द्वारे प्रवेश केला जातो, जसे की आमच्या बाबतीत वापरकर्ता खाते डेटा. परंतु कधीकधी, ब्राउझरमध्ये चालणाऱ्या क्लायंट अॅपवर काही डेटा टिकवून ठेवणे देखील मनोरंजक असते, चांगल्या वापरकर्ता अनुभवासाठी किंवा लोडिंग कार्यक्षमता सुधारण्यासाठी.
जेव्हा तुम्हाला तुमच्या ब्राउझरमध्ये डेटा टिकवून ठेवायचा असतो, तेव्हा तुम्ही स्वतःला काही महत्त्वाचे प्रश्न विचारले पाहिजेत:
जेव्हा तुम्हाला तुमच्या ब्राउझरमध्ये डेटा टिकवून ठेवायचा असेल, तेव्हा तुम्ही स्वतःला काही महत्त्वाचे प्रश्न विचारले पाहिजेत:
- *डेटा संवेदनशील आहे का?* तुम्ही क्लायंटवर कोणताही संवेदनशील डेटा संग्रहित करण्याचे टाळले पाहिजे, जसे की वापरकर्ता पासवर्ड.
- *तुम्हाला किती काळ हा डेटा ठेवायचा आहे?* तुम्ही हा डेटा फक्त चालू सत्रासाठी प्रवेश करण्याची योजना आखत आहात की तुम्हाला तो कायमस्वरूपी संग्रहित करायचा आहे?
- *डेटा संवेदनशील आहे का?* तुम्ही क्लायंटवर कोणताही संवेदनशील डेटा संग्रहित करणे टाळले पाहिजे, जसे की वापरकर्ता पासवर्ड.
- *तुम्हाला हा डेटा किती काळ ठेवायचा आहे?* तुम्ही हा डेटा फक्त चालू सत्रासाठी प्रवेश करण्याची योजना आखत आहात की तुम्हाला तो कायमस्वरूपी संग्रहित करायचा आहे?
वेब अॅपमध्ये माहिती संग्रहित करण्याचे अनेक मार्ग आहेत, तुम्हाला काय साध्य करायचे आहे यावर अवलंबून. उदाहरणार्थ, तुम्ही शोध क्वेरी संग्रहित करण्यासाठी URL वापरू शकता आणि ते वापरकर्त्यांमध्ये शेअर करण्यायोग्य बनवू शकता. तुम्ही [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) देखील वापरू शकता जर डेटा सर्व्हरसह शेअर करणे आवश्यक असेल, जसे की [authentication](https://en.wikipedia.org/wiki/Authentication) माहिती.
वेब अॅपमध्ये माहिती संग्रहित करण्याचे अनेक मार्ग आहेत, तुम्हाला काय साध्य करायचे आहे यावर अवलंबून. उदाहरणार्थ, तुम्ही शोध क्वेरी संग्रहित करण्यासाठी URL वापरू शकता आणि ती वापरकर्त्यांमध्ये शेअर करण्यायोग्य बनवू शकता. जर डेटा सर्व्हरसह शेअर करणे आवश्यक असेल, जसे की [authentication](https://en.wikipedia.org/wiki/Authentication) माहिती, तर तुम्ही [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) वापरू शकता.
डेटा संग्रहित करण्यासाठी अनेक ब्राउझर APIs आहेत. त्यापैकी दोन विशेषतः मनोरंजक आहेत:
ब्राउझर API चा वापर करून डेटा संग्रहित करण्याचा आणखी एक पर्याय आहे. यापैकी दोन विशेषतः मनोरंजक आहेत:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): एक [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) जो सध्याच्या वेबसाइटसाठी विशिष्ट डेटा वेगवेगळ्या सत्रांमध्ये टिकवून ठेवतो. यात संग्रहित डेटा कधीही कालबाह्य होत नाही.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): हे `localStorage` प्रमाणेच कार्य करते परंतु यात संग्रहित डेटा सत्र संपल्यावर (ब्राउझर बंद झाल्यावर) साफ केला जातो.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): एक [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) जे सध्याच्या वेबसाइटसाठी डेटा वेगवेगळ्या सत्रांमध्ये टिकवून ठेवण्याची परवानगी देते. यात संग्रहित डेटा कधीही कालबाह्य होत नाही.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): हे `localStorage` प्रमाणेच कार्य करते परंतु यात संग्रहित डेटा सत्र संपल्यावर (ब्राउझर बंद केल्यावर) साफ केला जातो.
लक्षात घ्या की या दोन्ही APIs फक्त [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) संग्रहित करण्याची परवानगी देतात. जर तुम्हाला जटिल ऑब्जेक्ट्स संग्रहित करायचे असतील, तर तुम्हाला [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) वापरून ते [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) स्वरूपात सिरीयलाइझ करावे लागेल.
टीप की या दोन्ही API फक्त [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) साठवण्याची परवानगी देतात. जर तुम्हाला जटिल ऑब्जेक्ट्स संग्रहित करायचे असतील, तर तुम्हाला [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) वापरून ते [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) स्वरूपात सिरीयलाइझ करावे लागेल.
✅ जर तुम्हाला सर्व्हरशिवाय कार्य करणारे वेब अॅप तयार करायचे असेल, तर [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) वापरून क्लायंटवर डेटाबेस तयार करणे देखील शक्य आहे. हे प्रगत उपयोग प्रकरणांसाठी किंवा तुम्हाला मोठ्या प्रमाणात डेटा संग्रहित करायचा असल्यास राखीव आहे, कारण ते वापरण्यास अधिक जटिल आहे.
✅ जर तुम्हाला सर्व्हरशिवाय कार्य करणारे वेब अॅप तयार करायचे असेल, तर [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) वापरून क्लायंटवर डेटाबेस तयार करणे देखील शक्य आहे. हे प्रगत वापर प्रकरणांसाठी राखीव आहे किंवा तुम्हाला लक्षणीय प्रमाणात डेटा संग्रहित करायचा असल्यास, कारण ते वापरण्यास अधिक जटिल आहे.
### कार्य
आम्हाला आमच्या वापरकर्त्यांना*Logout* बटणावर स्पष्टपणे क्लिक करेपर्यंत लॉग इन ठेवायचे आहे, त्यामुळे आम्ही खाते डेटा संग्रहित करण्यासाठी `localStorage` वापरू. प्रथम, आपण डेटा संग्रहित करण्यासाठी वापरण्यासाठी एक की परिभाषित करूया.
आपण इच्छितो की आमचे वापरकर्ते*Logout* बटणावर स्पष्टपणे क्लिक करेपर्यंत लॉग इन राहावे, म्हणून आम्ही `localStorage` वापरून खाते डेटा संग्रहित करू. प्रथम, आपण डेटा संग्रहित करण्यासाठी वापरण्यासाठी एक की परिभाषित करूया.
```js
const storageKey = 'savedAccount';
```
मग`updateState()` फंक्शनच्या शेवटी ही ओळ जोडा:
यानंतर`updateState()` फंक्शनच्या शेवटी ही ओळ जोडा:
यामुळे, वापरकर्ता खाते डेटा टिकवून ठेवला जाईल आणि आपण पूर्वी केंद्रीकृत केलेल्या सर्व स्टेट अपडेट्समुळे नेहमी अद्ययावत राहील. यामुळे आपल्याला पूर्वी केलेल्या सर्व रिफॅक्टरिंगचा फायदा मिळायला सुरुवात होते 🙂.
यामुळे, वापरकर्त्याचे खाते डेटा टिकवून ठेवले जाईल आणि आपण यापूर्वी केंद्रीकृत केलेल्या सर्व स्टेट अद्यतनांमुळे नेहमी अद्ययावत राहील. यामुळे आपल्याला केलेल्या सर्व पुनर्रचनेचा फायदा होतो 🙂.
डेटा सेव्ह केला जात असल्याने, अॅप लोड झाल्यावर तो पुनर्संचयित करण्याची काळजी घेणे देखील आवश्यक आहे. आता आपल्याकडे अधिक इनिशियलायझेशन कोड असणार आहे, त्यामुळे `app.js` च्या तळाशी असलेल्या पूर्वीच्या कोडसह नवीन `init` फंक्शन तयार करणे चांगली कल्पना असू शकते:
डेटा जतन केला जात असल्याने, अॅप लोड केल्यावर तो पुनर्संचयित करण्याची देखील काळजी घ्यावी लागेल. आता आपल्याकडे अधिक प्रारंभिक कोड असणार आहे, म्हणून `init` नावाचे नवीन फंक्शन तयार करणे चांगले होईल, ज्यामध्ये `app.js` च्या तळाशी असलेला आपला पूर्वीचा कोड देखील समाविष्ट असेल:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
येथे आम्ही सेव्ह केलेला डेटा पुनर्प्राप्त करतो आणि जर काही असेल तर आम्ही त्यानुसार स्टेट अपडेट करतो. हे *route* अपडेट करण्यापूर्वी करणे महत्त्वाचे आहे, कारण पृष्ठ अपडेट दरम्यान स्टेटवर अवलंबून असलेला कोड असू शकतो.
येथे आम्ही जतन केलेला डेटा पुनर्प्राप्त करतो आणि जर काही डेटा सापडला तर आम्ही त्यानुसार स्टेट अद्यतनित करतो. हे *रूट* अद्यतनित करण्यापूर्वी करणे महत्त्वाचे आहे, कारण पृष्ठ अद्यतनादरम्यान स्टेटवर अवलंबून असलेला कोड असू शकतो.
आम्ही *Dashboard* पृष्ठ आमच्या अॅप्लिकेशनचे डिफॉल्ट पृष्ठ बनवू शकतो, कारण आता आम्ही खाते डेटा टिकवून ठेवत आहोत. जर कोणताही डेटा सापडला नाही, तर डॅशबोर्ड लॉगिन पृष्ठावर रीडायरेक्ट करण्याची काळजी घेतो. `updateRoute()` मध्ये, फॉलबॅक `return navigate('/login');` बदला `return navigate('/dashboard');`.
आता आपण *Dashboard* पृष्ठाला आपल्या अॅप्लिकेशनचे डिफॉल्ट पृष्ठ बनवू शकतो, कारण आपण आता खाते डेटा टिकवून ठेवत आहोत. जर कोणताही डेटा सापडला नाही, तर डॅशबोर्ड *Login* पृष्ठावर पुनर्निर्देशित करण्याची काळजी घेतो. `updateRoute()` मध्ये, फॉलबॅक `return navigate('/login');` बदलून `return navigate('/dashboard');` करा.
आता अॅपमध्ये लॉगिन करा आणि पृष्ठ रिफ्रेश करण्याचा प्रयत्न करा. तुम्ही डॅशबोर्डवरच राहाल. या अपडेटसह आपण आपल्या सर्व प्रारंभिक समस्यांचे निराकरण केले आहे...
आता अॅपमध्ये लॉगिन करा आणि पृष्ठ रीफ्रेश करण्याचा प्रयत्न करा. तुम्ही डॅशबोर्डवरच राहाल. या अद्यतनासह आपण आपल्या सर्व प्रारंभिक समस्यांचे निराकरण केले आहे...
## डेटा रिफ्रेश करा
## डेटा रीफ्रेश करा
...परंतु आपण नवीन समस्या निर्माण केली असावी. ओह!
...पण आपण नवीन समस्या निर्माण केली असू शकतो. ओह!
`test` खाते वापरून डॅशबोर्डवर जा, नंतर टर्मिनलवर नवीन व्यवहार तयार करण्यासाठी हा आदेश चालवा:
`test` खाते वापरून डॅशबोर्डवर जा, नंतर टर्मिनलवर हा आदेश चालवून नवीन व्यवहार तयार करा:
आता ब्राउझरमध्ये डॅशबोर्ड पृष्ठ रिफ्रेश करण्याचा प्रयत्न करा. काय होते? तुम्हाला नवीन व्यवहार दिसतो का?
आता ब्राउझरमध्ये तुमचे डॅशबोर्ड पृष्ठ रीफ्रेश करण्याचा प्रयत्न करा. काय होते? तुम्हाला नवीन व्यवहार दिसतो का?
`localStorage` मुळे स्टेट अनिश्चित काळासाठी टिकवून ठेवली जाते, परंतु याचा अर्थ असा आहे की तुम्ही अॅपमधून लॉगआउट करून पुन्हा लॉगिन केल्याशिवाय ती कधीही अपडेट होत नाही!
`localStorage` मुळे स्टेट अनिश्चित काळासाठी टिकवून ठेवली जाते, परंतु याचा अर्थ असा आहे की तुम्ही अॅपमधून लॉगआउट आणि पुन्हा लॉगिन केल्याशिवाय ती कधीही अद्यतनित होत नाही!
यावर उपाय करण्यासाठी एक संभाव्य रणनीती म्हणजे डॅशबोर्ड लोड झाल्यावर प्रत्येक वेळी खाते डेटा पुन्हा लोड करणे, जेणेकरून जुना डेटा टाळता येईल.
हे टाळण्यासाठी एक संभाव्य रणनीती म्हणजे डॅशबोर्ड लोड केल्यावर प्रत्येक वेळी खाते डेटा पुन्हा लोड करणे, जेणेकरून डेटा जुना होणार नाही.
### कार्य
@ -258,31 +258,18 @@ async function refresh() {
}
```
हे खाते डेटा अपडेट करते, नंतर डॅशबोर्ड पृष्ठाच्या HTML अपडेट करण्याची काळजी घेते. हे डॅशबोर्ड route लोड झाल्यावर कॉल करणे आवश्यक आहे. route डिफिनिशन अपडेट करा:
हे खाते डेटा अद्यतनित करते, नंतर डॅशबोर्ड पृष्ठाच्या HTML चे अद्यतनित करण्याची काळजी घेते. डॅशबोर्ड रूट लोड झाल्यावर आम्हाला कॉल करायचे आहे तेच आहे. रूट
आता डॅशबोर्ड रिफ्रेश करण्याचा प्रयत्न करा, ते अपडेट केलेला खाते डेटा प्रदर्शित करेल.
---
## 🚀 आव्हान
आता आपण डॅशबोर्ड लोड झाल्यावर प्रत्येक वेळी खाते डेटा पुन्हा लोड करतो, तुम्हाला असे वाटते का की आपल्याला *संपूर्ण खाते* डेटा टिकवून ठेवण्याची आवश्यकता आहे?
## असाइनमेंट
`localStorage` मध्ये काय सेव्ह केले जाते आणि लोड केले जाते ते फक्त अॅप
[ट्रान्झॅक्शन जोडण्याचा संवाद लागू करा](assignment.md)
["Add transaction" संवाद लागू करा](assignment.md)
खालील उदाहरणात असाइनमेंट पूर्ण केल्यानंतरचा परिणाम दाखवला आहे:
असाइनमेंट पूर्ण केल्यानंतरचा एक उदाहरण परिणाम येथे दिला आहे:

---
**अस्वीकरण**:
हा दस्तऐवज AI भाषांतर सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) चा वापर करून भाषांतरित करण्यात आला आहे. आम्ही अचूकतेसाठी प्रयत्नशील असलो तरी कृपया लक्षात ठेवा की स्वयंचलित भाषांतरे त्रुटी किंवा अचूकतेच्या अभावाने युक्त असू शकतात. मूळ भाषेतील दस्तऐवज हा अधिकृत स्रोत मानला जावा. महत्त्वाच्या माहितीसाठी व्यावसायिक मानवी भाषांतराची शिफारस केली जाते. या भाषांतराचा वापर करून उद्भवलेल्या कोणत्याही गैरसमज किंवा चुकीच्या अर्थासाठी आम्ही जबाबदार राहणार नाही.
हा दस्तऐवज AI भाषांतर सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) वापरून भाषांतरित करण्यात आला आहे. आम्ही अचूकतेसाठी प्रयत्नशील असलो तरी, कृपयास लक्षात ठेवा की स्वयंचलित भाषांतरांमध्ये त्रुटी किंवा अचूकतेचा अभाव असू शकतो. मूळ भाषेतील दस्तऐवज हा अधिकृत स्रोत मानला जावा. महत्त्वाच्या माहितीसाठी व्यावसायिक मानवी भाषांतराची शिफारस केली जाते. या भाषांतराचा वापर करून निर्माण होणाऱ्या कोणत्याही गैरसमज किंवा चुकीच्या अर्थासाठी आम्ही जबाबदार राहणार नाही.
# Membina Aplikasi Perbankan Bahagian 4: Konsep Pengurusan Keadaan
# Bina Aplikasi Perbankan Bahagian 4: Konsep Pengurusan Keadaan
## Kuiz Pra-Kuliah
@ -15,15 +15,15 @@ CO_OP_TRANSLATOR_METADATA:
### Pengenalan
Apabila aplikasi web berkembang, ia menjadi cabaran untuk menjejaki semua aliran data. Kod mana yang mendapatkan data, halaman mana yang menggunakannya, di mana dan bila ia perlu dikemas kini...mudah untuk berakhir dengan kod yang berselerak dan sukar untuk diselenggara. Ini terutamanya benar apabila anda perlu berkongsi data antara halaman yang berbeza dalam aplikasi anda, contohnya data pengguna. Konsep *pengurusan keadaan* sentiasa wujud dalam semua jenis program, tetapi apabila aplikasi web terus berkembang dalam kerumitan, ia kini menjadi titik utama untuk difikirkan semasa pembangunan.
Apabila aplikasi web berkembang, ia menjadi cabaran untuk menjejaki semua aliran data. Kod mana yang mendapatkan data, halaman mana yang menggunakannya, di mana dan bila ia perlu dikemas kini...mudah untuk berakhir dengan kod yang berselerak dan sukar untuk diselenggara. Ini terutamanya benar apabila anda perlu berkongsi data antara halaman yang berbeza dalam aplikasi anda, contohnya data pengguna. Konsep *pengurusan keadaan* sentiasa wujud dalam semua jenis program, tetapi apabila aplikasi web terus berkembang dalam kerumitan, ia kini menjadi titik utama yang perlu difikirkan semasa pembangunan.
Dalam bahagian terakhir ini, kita akan melihat semula aplikasi yang telah kita bina untuk memikirkan semula bagaimana keadaan diuruskan, membolehkan sokongan untuk penyegaran pelayar pada bila-bila masa, dan mengekalkan data merentasi sesi pengguna.
Dalam bahagian terakhir ini, kita akan meninjau semula aplikasi yang telah kita bina untuk memikirkan semula bagaimana keadaan diuruskan, membolehkan sokongan untuk penyegaran pelayar pada bila-bila masa, dan mengekalkan data merentasi sesi pengguna.
### Prasyarat
Anda perlu telah menyelesaikan bahagian [pengambilan data](../3-data/README.md) aplikasi web untuk pelajaran ini. Anda juga perlu memasang [Node.js](https://nodejs.org) dan [menjalankan API pelayan](../api/README.md) secara tempatan supaya anda boleh menguruskan data akaun.
Anda perlu telah melengkapkan bahagian [pengambilan data](../3-data/README.md) aplikasi web untuk pelajaran ini. Anda juga perlu memasang [Node.js](https://nodejs.org) dan [menjalankan API pelayan](../api/README.md) secara tempatan supaya anda boleh menguruskan data akaun.
Anda boleh menguji bahawa pelayan berjalan dengan betul dengan melaksanakan arahan ini dalam terminal:
Anda boleh menguji sama ada pelayan berjalan dengan betul dengan melaksanakan perintah ini dalam terminal:
```sh
curl http://localhost:5000/api
@ -32,7 +32,7 @@ curl http://localhost:5000/api
---
## Memikirkan semula pengurusan keadaan
## Fikirkan semula pengurusan keadaan
Dalam [pelajaran sebelumnya](../3-data/README.md), kami memperkenalkan konsep asas keadaan dalam aplikasi kami dengan pembolehubah global `account` yang mengandungi data bank untuk pengguna yang sedang log masuk. Walau bagaimanapun, pelaksanaan semasa kami mempunyai beberapa kelemahan. Cuba segarkan halaman apabila anda berada di papan pemuka. Apa yang berlaku?
@ -40,26 +40,26 @@ Terdapat 3 isu dengan kod semasa:
- Keadaan tidak dikekalkan, kerana penyegaran pelayar membawa anda kembali ke halaman log masuk.
- Terdapat pelbagai fungsi yang mengubah keadaan. Apabila aplikasi berkembang, ia boleh menyukarkan untuk menjejaki perubahan dan mudah untuk terlupa mengemas kini satu.
- Keadaan tidak dibersihkan, jadi apabila anda klik pada*Logout*, data akaun masih ada walaupun anda berada di halaman log masuk.
- Keadaan tidak dibersihkan, jadi apabila anda mengklik *Logout*, data akaun masih ada walaupun anda berada di halaman log masuk.
Kami boleh mengemas kini kod kami untuk menangani isu-isu ini satu persatu, tetapi ia akan mencipta lebih banyak penduaan kod dan menjadikan aplikasi lebih kompleks dan sukar untuk diselenggara. Atau kami boleh berhenti seketika dan memikirkan semula strategi kami.
> Apakah masalah yang sebenarnya kami cuba selesaikan di sini?
> Apakah masalah yang sebenarnya kita cuba selesaikan di sini?
[Pengurusan keadaan](https://en.wikipedia.org/wiki/State_management) adalah tentang mencari pendekatan yang baik untuk menyelesaikan dua masalah tertentu ini:
- Bagaimana untuk memastikan aliran data dalam aplikasi mudah difahami?
- Bagaimana untuk memastikan data keadaan sentiasa selaras dengan antara muka pengguna (dan sebaliknya)?
Setelah anda menjaga perkara ini, sebarang isu lain yang mungkin anda hadapi mungkin telah diselesaikan atau menjadi lebih mudah untuk diperbaiki. Terdapat banyak pendekatan yang mungkin untuk menyelesaikan masalah ini, tetapi kami akan menggunakan penyelesaian biasa yang terdiri daripada **memusatkan data dan cara untuk mengubahnya**. Aliran data akan berjalan seperti ini:
Setelah anda menyelesaikan perkara ini, sebarang isu lain yang mungkin anda hadapi sama ada telah diselesaikan atau menjadi lebih mudah untuk diselesaikan. Terdapat banyak pendekatan yang mungkin untuk menyelesaikan masalah ini, tetapi kami akan menggunakan penyelesaian biasa yang terdiri daripada **memusatkan data dan cara untuk mengubahnya**. Aliran data akan berjalan seperti ini:

> Kami tidak akan membincangkan di sini bahagian di mana data secara automatik mencetuskan kemas kini paparan, kerana ia berkaitan dengan konsep yang lebih maju tentang [Pengaturcaraan Reaktif](https://en.wikipedia.org/wiki/Reactive_programming). Ia adalah subjek susulan yang baik jika anda ingin mendalami.
> Kami tidak akan membincangkan bahagian di mana data secara automatik mencetuskan kemas kini paparan, kerana ia berkaitan dengan konsep lanjutan [Pengaturcaraan Reaktif](https://en.wikipedia.org/wiki/Reactive_programming). Ia adalah subjek susulan yang baik jika anda ingin mendalami.
✅ Terdapat banyak perpustakaan di luar sana dengan pendekatan yang berbeza untuk pengurusan keadaan, [Redux](https://redux.js.org) menjadi pilihan yang popular. Lihat konsep dan corak yang digunakan kerana ia sering menjadi cara yang baik untuk belajar tentang isu-isu yang mungkin anda hadapi dalam aplikasi web besar dan bagaimana ia boleh diselesaikan.
### Tugasan
### Tugas
Kita akan mulakan dengan sedikit penstrukturan semula. Gantikan deklarasi `account`:
@ -75,7 +75,7 @@ let state = {
};
```
Idea di sini adalah untuk *memusatkan* semua data aplikasi kami dalam satu objek keadaan. Buat masa ini kami hanya mempunyai `account` dalam keadaan, jadi ia tidak banyak berubah, tetapi ia mencipta laluan untuk evolusi.
Idea ini adalah untuk *memusatkan* semua data aplikasi kami dalam satu objek keadaan. Kami hanya mempunyai `account` buat masa ini dalam keadaan jadi ia tidak banyak berubah, tetapi ia mencipta laluan untuk evolusi.
Kami juga perlu mengemas kini fungsi yang menggunakannya. Dalam fungsi `register()` dan `login()`, gantikan `account = ...` dengan `state.account = ...`;
@ -85,21 +85,21 @@ Di bahagian atas fungsi `updateDashboard()`, tambahkan baris ini:
const account = state.account;
```
Penstrukturan semula ini sendiri tidak membawa banyak peningkatan, tetapi idea di sini adalah untuk meletakkan asas untuk perubahan seterusnya.
Penstrukturan semula ini sendiri tidak membawa banyak penambahbaikan, tetapi idea ini adalah untuk meletakkan asas untuk perubahan seterusnya.
## Menjejaki perubahan data
## Jejak perubahan data
Sekarang kita telah meletakkan objek `state` untuk menyimpan data kita, langkah seterusnya adalah untuk memusatkan kemas kini. Tujuannya adalah untuk memudahkan menjejaki sebarang perubahan dan bila ia berlaku.
Sekarang kita telah meletakkan objek `state` untuk menyimpan data kita, langkah seterusnya adalah untuk memusatkan kemas kini. Matlamatnya adalah untuk memudahkan menjejaki sebarang perubahan dan bila ia berlaku.
Untuk mengelakkan perubahan dibuat pada objek `state`, adalah juga amalan yang baik untuk menganggapnya [*tidak boleh diubah*](https://en.wikipedia.org/wiki/Immutable_object), bermaksud ia tidak boleh diubah sama sekali. Ini juga bermaksud anda perlu mencipta objek keadaan baru jika anda ingin mengubah apa-apa di dalamnya. Dengan melakukan ini, anda membina perlindungan terhadap [kesan sampingan](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) yang berpotensi tidak diingini, dan membuka kemungkinan untuk ciri baru dalam aplikasi anda seperti melaksanakan undo/redo, sambil juga memudahkan debugging. Sebagai contoh, anda boleh log setiap perubahan yang dibuat pada keadaan dan menyimpan sejarah perubahan untuk memahami punca bug.
Untuk mengelakkan perubahan dibuat pada objek `state`, adalah juga amalan yang baik untuk menganggapnya [*tidak boleh diubah*](https://en.wikipedia.org/wiki/Immutable_object), yang bermaksud ia tidak boleh diubah sama sekali. Ini juga bermakna anda perlu mencipta objek keadaan baharu jika anda ingin mengubah apa-apa di dalamnya. Dengan melakukan ini, anda membina perlindungan terhadap [kesan sampingan](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) yang berpotensi tidak diingini, dan membuka kemungkinan untuk ciri baharu dalam aplikasi anda seperti melaksanakan undo/redo, sambil juga memudahkan penyahpepijatan. Sebagai contoh, anda boleh log setiap perubahan yang dibuat pada keadaan dan menyimpan sejarah perubahan untuk memahami punca pepijat.
Dalam JavaScript, anda boleh menggunakan [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) untuk mencipta versi tidak boleh diubah bagi objek. Jika anda cuba membuat perubahan pada objek tidak boleh diubah, pengecualian akan dinaikkan.
Dalam JavaScript, anda boleh menggunakan [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) untuk mencipta versi objek yang tidak boleh diubah. Jika anda cuba membuat perubahan pada objek yang tidak boleh diubah, pengecualian akan dinaikkan.
✅ Adakah anda tahu perbezaan antara objek tidak boleh diubah *shallow* dan *deep*? Anda boleh membacanya [di sini](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Adakah anda tahu perbezaan antara objek *shallow* dan *deep* yang tidak boleh diubah? Anda boleh membacanya [di sini](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Tugasan
### Tugas
Mari kita cipta fungsi baru `updateState()`:
Mari kita cipta fungsi baharu `updateState()`:
```js
function updateState(property, newData) {
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
Dalam fungsi ini, kami mencipta objek keadaan baru dan menyalin data dari keadaan sebelumnya menggunakan [*operator spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kemudian kami menimpa sifat tertentu objek keadaan dengan data baru menggunakan [notasi kurungan](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` untuk tugasan. Akhirnya, kami mengunci objek untuk mengelakkan pengubahsuaian menggunakan `Object.freeze()`. Buat masa ini kami hanya mempunyai sifat `account` yang disimpan dalam keadaan, tetapi dengan pendekatan ini anda boleh menambah sebanyak mana sifat yang anda perlukan dalam keadaan.
Dalam fungsi ini, kami mencipta objek keadaan baharu dan menyalin data daripada keadaan sebelumnya menggunakan [*operator spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kemudian kami menimpa sifat tertentu objek keadaan dengan data baharu menggunakan [notasi kurungan](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` untuk tugasan. Akhir sekali, kami mengunci objek untuk mengelakkan pengubahsuaian menggunakan `Object.freeze()`. Buat masa ini, kami hanya mempunyai sifat `account` yang disimpan dalam keadaan, tetapi dengan pendekatan ini anda boleh menambah sebanyak mana sifat yang anda perlukan dalam keadaan.
Kami juga akan mengemas kini inisialisasi `state` untuk memastikan keadaan awal juga dibekukan:
@ -132,9 +132,9 @@ Lakukan perkara yang sama dengan fungsi `login`, menggantikan `state.account = d
updateState('account', data);
```
Kami kini akan mengambil peluang untuk memperbaiki isu data akaun yang tidak dibersihkan apabila pengguna klik pada*Logout*.
Kami kini akan mengambil peluang untuk membetulkan isu data akaun yang tidak dibersihkan apabila pengguna mengklik *Logout*.
Cipta fungsi baru `logout()`:
Cipta fungsi baharu `logout()`:
```js
function logout() {
@ -145,33 +145,33 @@ function logout() {
Dalam `updateDashboard()`, gantikan pengalihan `return navigate('/login');` dengan `return logout()`;
Cuba daftar akaun baru, log keluar dan log masuk semula untuk memastikan semuanya masih berfungsi dengan betul.
Cuba daftar akaun baharu, log keluar dan log masuk semula untuk memastikan semuanya masih berfungsi dengan betul.
> Tip: anda boleh melihat semua perubahan keadaan dengan menambah `console.log(state)` di bahagian bawah `updateState()` dan membuka konsol dalam alat pembangunan pelayar anda.
> Petua: anda boleh melihat semua perubahan keadaan dengan menambah `console.log(state)` di bahagian bawah `updateState()` dan membuka konsol dalam alat pembangunan pelayar anda.
## Mengekalkan keadaan
## Kekalkan keadaan
Kebanyakan aplikasi web perlu mengekalkan data untuk dapat berfungsi dengan betul. Semua data kritikal biasanya disimpan dalam pangkalan data dan diakses melalui API pelayan, seperti data akaun pengguna dalam kes kami. Tetapi kadang-kadang, ia juga menarik untuk mengekalkan beberapa data pada aplikasi klien yang berjalan dalam pelayar anda, untuk pengalaman pengguna yang lebih baik atau untuk meningkatkan prestasi pemuatan.
Kebanyakan aplikasi web perlu mengekalkan data untuk dapat berfungsi dengan betul. Semua data kritikal biasanya disimpan dalam pangkalan data dan diakses melalui API pelayan, seperti data akaun pengguna dalam kes kami. Tetapi kadangkala, adalah menarik untuk mengekalkan beberapa data pada aplikasi klien yang berjalan dalam pelayar anda, untuk pengalaman pengguna yang lebih baik atau untuk meningkatkan prestasi pemuatan.
Apabila anda ingin mengekalkan data dalam pelayar anda, terdapat beberapa soalan penting yang perlu anda tanyakan kepada diri sendiri:
- *Adakah data sensitif?* Anda harus mengelakkan menyimpan sebarang data sensitif pada klien, seperti kata laluan pengguna.
- *Adakah data itu sensitif?* Anda harus mengelakkan daripada menyimpan sebarang data sensitif pada klien, seperti kata laluan pengguna.
- *Berapa lama anda perlu menyimpan data ini?* Adakah anda merancang untuk mengakses data ini hanya untuk sesi semasa atau adakah anda mahu ia disimpan selama-lamanya?
Terdapat pelbagai cara untuk menyimpan maklumat dalam aplikasi web, bergantung pada apa yang anda ingin capai. Sebagai contoh, anda boleh menggunakan URL untuk menyimpan pertanyaan carian, dan menjadikannya boleh dikongsi antara pengguna. Anda juga boleh menggunakan [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) jika data perlu dikongsi dengan pelayan, seperti maklumat [pengesahan](https://en.wikipedia.org/wiki/Authentication).
Terdapat pelbagai cara untuk menyimpan maklumat dalam aplikasi web, bergantung pada apa yang anda ingin capai. Sebagai contoh, anda boleh menggunakan URL untuk menyimpan pertanyaan carian, dan menjadikannya boleh dikongsi antara pengguna. Anda juga boleh menggunakan [kuki HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) jika data perlu dikongsi dengan pelayan, seperti maklumat [pengesahan](https://en.wikipedia.org/wiki/Authentication).
Pilihan lain adalah menggunakan salah satu daripada banyak API pelayar untuk menyimpan data. Dua daripadanya sangat menarik:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): sebuah [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) yang membolehkan untuk mengekalkan data khusus untuk laman web semasa merentasi sesi yang berbeza. Data yang disimpan di dalamnya tidak pernah tamat tempoh.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): yang ini berfungsi sama seperti `localStorage` kecuali data yang disimpan di dalamnya akan dibersihkan apabila sesi tamat (apabila pelayar ditutup).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ini berfungsi sama seperti `localStorage` kecuali data yang disimpan di dalamnya akan dipadamkan apabila sesi tamat (apabila pelayar ditutup).
Perhatikan bahawa kedua-dua API ini hanya membenarkan untuk menyimpan [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Jika anda ingin menyimpan objek kompleks, anda perlu menyusunnya ke format [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) menggunakan [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Perhatikan bahawa kedua-dua API ini hanya membenarkan untuk menyimpan [rentetan](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Jika anda ingin menyimpan objek kompleks, anda perlu menyusunnya ke format [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) menggunakan [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Jika anda ingin mencipta aplikasi web yang tidak berfungsi dengan pelayan, adalah juga mungkin untuk mencipta pangkalan data pada klien menggunakan API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Yang ini dikhaskan untuk kes penggunaan lanjutan atau jika anda perlu menyimpan sejumlah besar data, kerana ia lebih kompleks untuk digunakan.
✅ Jika anda ingin mencipta aplikasi web yang tidak berfungsi dengan pelayan, adalah juga mungkin untuk mencipta pangkalan data pada klien menggunakan API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ini dikhaskan untuk kes penggunaan lanjutan atau jika anda perlu menyimpan sejumlah besar data, kerana ia lebih kompleks untuk digunakan.
### Tugasan
### Tugas
Kami mahu pengguna kami kekal log masuk sehingga mereka secara eksplisit klik pada butang *Logout*, jadi kami akan menggunakan `localStorage` untuk menyimpan data akaun. Pertama, mari kita tentukan kunci yang akan kita gunakan untuk menyimpan data kita.
Kami mahu pengguna kami kekal log masuk sehingga mereka secara eksplisit mengklik butang *Logout*, jadi kami akan menggunakan `localStorage` untuk menyimpan data akaun. Pertama, mari kita tentukan kunci yang akan kita gunakan untuk menyimpan data kita.
Dengan ini, data akaun pengguna akan dikekalkan dan sentiasa terkini kerana kami telah memusatkan semua kemas kini keadaan kami sebelum ini. Di sinilah kami mula mendapat manfaat daripada semua penstrukturan semula kami sebelum ini 🙂.
Oleh kerana data disimpan, kami juga perlu mengambil kira pemulihannya apabila aplikasi dimuatkan. Oleh kerana kami akan mula mempunyai lebih banyak kod inisialisasi, mungkin idea yang baik untuk mencipta fungsi baru `init`, yang juga termasuk kod kami sebelum ini di bahagian bawah `app.js`:
Memandangkan data disimpan, kami juga perlu mengambil berat tentang memulihkannya apabila aplikasi dimuatkan. Oleh kerana kami akan mula mempunyai lebih banyak kod inisialisasi, mungkin idea yang baik untuk mencipta fungsi baharu `init`, yang juga termasuk kod kami sebelum ini di bahagian bawah `app.js`:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Di sini kami mengambil data yang disimpan, dan jika ada, kami mengemas kini keadaan sewajarnya. Adalah penting untuk melakukan ini *sebelum* mengemas kini laluan, kerana mungkin terdapat kod yang bergantung pada keadaan semasa kemas kini halaman.
Di sini kami mendapatkan data yang disimpan, dan jika ada, kami mengemas kini keadaan sewajarnya. Adalah penting untuk melakukan ini *sebelum* mengemas kini laluan, kerana mungkin terdapat kod yang bergantung pada keadaan semasa kemas kini halaman.
Kami juga boleh menjadikan halaman *Dashboard* sebagai halaman lalai aplikasi kami, kerana kami kini mengekalkan data akaun. Jika tiada data ditemui, papan pemuka akan menguruskan pengalihan ke halaman *Login*pula. Dalam `updateRoute()`, gantikan fallback `return navigate('/login');` dengan `return navigate('/dashboard');`.
Kami juga boleh menjadikan halaman *Dashboard* sebagai halaman lalai aplikasi kami, kerana kami kini mengekalkan data akaun. Jika tiada data ditemui, papan pemuka akan menguruskan pengalihan ke halaman *Login*juga. Dalam `updateRoute()`, gantikan fallback `return navigate('/login');` dengan `return navigate('/dashboard');`.
Sekarang log masuk ke aplikasi dan cuba segarkan halaman. Anda sepatutnya kekal di papan pemuka. Dengan kemas kini itu, kami telah menjaga semua isu awal kami...
Sekarang log masuk ke aplikasi dan cuba segarkan halaman. Anda sepatutnya kekal di papan pemuka. Dengan kemas kini itu, kami telah menyelesaikan semua isu awal kami...
## Menyegarkan data
## Segarkan data
...Tetapi kami mungkin juga telah mencipta isu baru. Oops!
...Tetapi kami mungkin juga telah mencipta isu baharu. Oops!
Pergi ke papan pemuka menggunakan akaun `test`, kemudian jalankan arahan ini pada terminal untuk mencipta transaksi baru:
Pergi ke papan pemuka menggunakan akaun `test`, kemudian jalankan perintah ini pada terminal untuk mencipta transaksi baharu:
Cuba segarkan halaman papan pemuka anda dalam pelayar sekarang. Apa yang berlaku? Adakah anda melihat transaksi baru?
Cuba segarkan halaman papan pemuka anda dalam pelayar sekarang. Apa yang berlaku? Adakah anda melihat transaksi baharu?
Keadaan dikekalkan tanpa had terima kasih kepada `localStorage`, tetapi itu juga bermaksud ia tidak pernah dikemas kini sehingga anda log keluar dari aplikasi dan log masuk semula!
Keadaan dikekalkan selama-lamanya terima kasih kepada `localStorage`, tetapi itu juga bermakna ia tidak pernah dikemas kini sehingga anda log keluar dari aplikasi dan log masuk semula!
Satu strategi yang mungkin untuk memperbaikinya adalah dengan memuat semula data akaun setiap kali papan pemuka dimuatkan, untuk mengelakkan data yang tidak terkini.
Satu strategi yang mungkin untuk membetulkannya adalah dengan memuat semula data akaun setiap kali papan pemuka dimuatkan, untuk mengelakkan data yang tidak terkini.
### Tugasan
### Tugas
Cipta fungsi baru `updateAccountData`:
Cipta fungsi baharu `updateAccountData`:
```js
async function updateAccountData() {
@ -249,7 +249,7 @@ async function updateAccountData() {
Kaedah ini memeriksa bahawa kami sedang log masuk kemudian memuat semula data akaun dari pelayan.
Cipta fungsi lain bernama `refresh`:
Cipta satu lagi fungsi bernama `refresh`:
```js
async function refresh() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
Yang ini mengemas kini data akaun, kemudian menguruskan kemas kini HTML halaman papan pemuka. Ia adalah apa yang perlu kita panggil apabila laluan papan pemuka dimuatkan. Kemas kini definisi laluan dengan:
Yang ini mengemas kini data akaun, kemudian menguruskan pengemaskinian HTML halaman papan pemuka. Inilah yang perlu kita panggil apabila laluan papan pemuka dimuatkan. Kemas kini definisi laluan dengan:
```js
const routes = {
@ -273,22 +273,22 @@ Cuba muat semula papan pemuka sekarang, ia sepatutnya memaparkan data akaun yang
## 🚀 Cabaran
Sekarang kita memuat semula data akaun setiap kali papan pemuka dimuatkan, adakah anda fikir kita masih perlu mengekalkan *semua data akaun*?
Sekarang bahawa kami memuat semula data akaun setiap kali papan pemuka dimuatkan, adakah anda fikir kami masih perlu mengekalkan *semua data akaun*?
Cuba bekerjasama untuk mengubah apa yang disimpan dan dimuatkan dari `localStorage` untuk hanya memasukkan apa yang benar-benar diperlukan untuk aplikasi berfungsi.
Berikut adalah contoh hasil selepas menyelesaikan tugasan:

---
**Penafian**:
Dokumen ini telah diterjemahkan menggunakan perkhidmatan terjemahan AI [Co-op Translator](https://github.com/Azure/co-op-translator). Walaupun kami berusaha untuk memastikan ketepatan, sila ambil maklum bahawa terjemahan automatik mungkin mengandungi kesilapan atau ketidaktepatan. Dokumen asal dalam bahasa asalnya harus dianggap sebagai sumber yang berwibawa. Untuk maklumat yang kritikal, terjemahan manusia profesional adalah disyorkan. Kami tidak bertanggungjawab atas sebarang salah faham atau salah tafsir yang timbul daripada penggunaan terjemahan ini.
Dokumen ini telah diterjemahkan menggunakan perkhidmatan terjemahan AI [Co-op Translator](https://github.com/Azure/co-op-translator). Walaupun kami berusaha untuk memastikan ketepatan, sila ambil perhatian bahawa terjemahan automatik mungkin mengandungi kesilapan atau ketidaktepatan. Dokumen asal dalam bahasa asalnya harus dianggap sebagai sumber yang berwibawa. Untuk maklumat yang kritikal, terjemahan manusia profesional adalah disyorkan. Kami tidak bertanggungjawab atas sebarang salah faham atau salah tafsir yang timbul daripada penggunaan terjemahan ini.
- state ကို ထိန်းသိမ်းထားခြင်းမရှိသဖြင့် browser refresh လုပ်လိုက်သည်နှင့် login စာမျက်နှာသို့ ပြန်သွားသည်။
- state ကို ပြောင်းလဲသည့် function များစွာ ရှိသည်။ အက်ပ်ကြီးလာသည်နှင့် အပြောင်းအလဲများကို စဉ်းစားရန် ခက်ခဲလာပြီး function တစ်ခုကို အပ်ဒိတ်လုပ်ရန် မေ့နိုင်သည်။
- state ကို သန့်ရှင်းမလုပ်သဖြင့် *Logout* ကို နှိပ်လိုက်သည်နှင့် login စာမျက်နှာတွင် ရောက်နေသော်လည်း account data သေးသေးလေး ရှိနေသည်။
- state data ကို အသုံးပြုသူ interface နှင့် အမြဲအချိန်တိုင်အောင် (vice versa) ဘယ်လို sync လုပ်မလဲ?
[State management](https://en.wikipedia.org/wiki/State_management) ဆိုတာ app ရဲ့ data flow တွေကိုနားလည်ရလွယ်ကူတဲ့နည်းလမ်းကိုရှာဖွေဖို့နှင့် state data ကိုအသုံးပြုသူ interface နဲ့အမြဲတန်းအညီအမျှထားဖို့ (နှင့်အတူတူ) ဘယ်လိုလုပ်ရမလဲဆိုတာကိုအဓိကထားတဲ့အရာဖြစ်ပါတယ်။


အဓိကအကြောင်းအရာက app ရဲ့ data အားလုံးကို state object တစ်ခုထဲမှာစုစည်းဖို့ဖြစ်ပါတယ်။ လက်ရှိမှာ state မှာ `account` တစ်ခုသာရှိတာကြောင့် များစွာပြောင်းလဲမှုမရှိပါဘူး၊ ဒါပေမယ့်အနာဂတ်အတွက်လမ်းကြောင်းဖန်တီးပေးပါတယ်။
ဒါ့အပြင်၊ function များကို update လုပ်ရန် လိုအပ်သည်။ `register()` နှင့် `login()` function များတွင် `account = ...` ကို `state.account = ...` ဖြင့် အစားထိုးပါ။
ဒါ့အပြင်၊ ဒါကိုအသုံးပြုတဲ့ function တွေကိုလည်း update လုပ်ဖို့လိုအပ်ပါတယ်။ `register()` နှင့် `login()` function တွေမှာ `account = ...` ကို `state.account = ...` နဲ့အစားထိုးပါ။
`updateDashboard()` function ၏ အပေါ်တွင် အောက်ပါလိုင်းကို ထည့်ပါ-
`updateDashboard()` function ရဲ့အပေါ်ဆုံးမှာ ဒီလိုလိုင်းတစ်ခုထည့်ပါ-
State object ကို data ကိုသိမ်းဆည်းဖို့အသုံးပြုထားပြီးနောက်၊ ပြောင်းလဲမှုများကိုစုစည်းဖို့နောက်တစ်ဆင့်အဆင့်တက်ပါမယ်။ ပြောင်းလဲမှုများနှင့်အချိန်ကိုလွယ်ကူစွာစစ်ဆေးနိုင်ဖို့ရည်ရွယ်ပါတယ်။
> အကြံပြုချက်- `updateState()` ၏ အောက်ဆုံးတွင် `console.log(state)` ကို ထည့်ပြီး၊ browser development tools တွင် console ကို ဖွင့်ကာ state ပြောင်းလဲမှုအားလုံးကို ကြည့်ရှုနိုင်သည်။
> အကြံပြုချက်- state ပြောင်းလဲမှုအားလုံးကို browser ရဲ့ development tools မှာ console ကိုဖွင့်ပြီး `updateState()` ရဲ့အောက်ဆုံးမှာ `console.log(state)` ထည့်ပြီးကြည့်နိုင်ပါတယ်။
## state ကို ထိန်းသိမ်းခြင်း
## State ကိုတည်ရှိနေစေခြင်း
ဝက်ဘ်အက်ပ်များအများစုသည် data ကို ထိန်းသိမ်းရန် လိုအပ်သည်။ အရေးကြီးသော data အားလုံးကို database တွင် သိမ်းဆည်းပြီး၊ server API မှတဆင့် access လုပ်သည်။ သို့သော်၊ browser တွင် client app တွင် data တစ်ချို့ကို သိမ်းဆည်းခြင်းသည် အသုံးပြုသူအတွေ့အကြုံကို ပိုမိုကောင်းမွန်စေခြင်း သို့မဟုတ် loading performance ကို တိုးတက်စေခြင်းအတွက် စိတ်ဝင်စားစရာကောင်းသည်။
ဝက်ဘ်အက်ပ်များအများစုမှာ data ကိုတည်ရှိနေစေရန်လိုအပ်ပါတယ်။ အရေးကြီးသော data အားလုံးကို database မှာသိမ်းဆည်းပြီး server API မှတဆင့် access လုပ်ပါတယ်၊ ဥပမာအားဖြင့် အသုံးပြုသူ account data ကဲ့သို့။ သို့သော်လည်း၊ browser မှာ run လုပ်နေတဲ့ client app မှာ data တစ်ချို့ကိုတည်ရှိနေစေရန်လည်း sometimes အကျိုးရှိပါတယ်၊ user experience ကိုပိုမိုကောင်းမွန်စေဖို့ ဒါမှမဟုတ် loading performance ကိုတိုးတက်စေဖို့။
browser တွင် data ကို သိမ်းဆည်းလိုပါက၊ အရေးကြီးသော မေးခွန်းများကို မေးရမည်-
Browser မှာ data ကိုတည်ရှိနေစေချင်တဲ့အခါ၊ အရေးကြီးသောမေးခွန်းတစ်ချို့ကိုမေးဖို့လိုအပ်ပါတယ်-
- *ဒီ data သည် sensitive ဖြစ်ပါသလား?* အသုံးပြုသူ၏ password ကဲ့သို့သော sensitive data များကို client တွင် သိမ်းဆည်းရန် ရှောင်ရှားသင့်သည်။
- *ဒီ data ကို ဘယ်လောက်ကြာအောင် သိမ်းဆည်းလိုပါသလဲ?* ဒီ data ကို လက်ရှိ session အတွက်သာ အသုံးပြုလိုပါသလား၊ ဒါမှမဟုတ် အမြဲတမ်း သိမ်းဆည်းလိုပါသလား?
- *ဒီ data ကိုဘယ်လောက်ကြာကြာထားချင်ပါသလဲ?* ဒီ data ကိုလက်ရှိ session အတွက်သာ access လုပ်ချင်ပါသလား၊ ဒါမှမဟုတ် အမြဲတမ်းသိမ်းဆည်းထားချင်ပါသလား?
ဝက်ဘ်အက်ပ်တွင် data ကို သိမ်းဆည်းရန် နည်းလမ်းများစွာ ရှိသည်။ ဥပမာအားဖြင့်၊ search query ကို URL တွင် သိမ်းဆည်းပြီး၊ အသုံးပြုသူများအကြား မျှဝေနိုင်သည်။ [authentication](https://en.wikipedia.org/wiki/Authentication) အချက်အလက်ကဲ့သို့ server နှင့် data ကို မျှဝေလိုပါက [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ကို အသုံးပြုနိုင်သည်။
Web app အတွင်းမှာ data ကိုသိမ်းဆည်းဖို့နည်းလမ်းများစွာရှိပါတယ်၊ မိမိရဲ့ရည်ရွယ်ချက်အပေါ်မူတည်ပြီး။ ဥပမာအားဖြင့်၊ search query ကို URL တွေမှာသိမ်းဆည်းပြီး အသုံးပြုသူများအကြားမျှဝေနိုင်ပါတယ်။ [Authentication](https://en.wikipedia.org/wiki/Authentication) အချက်အလက်ကဲ့သို့ server နဲ့မျှဝေဖို့လိုအပ်တဲ့ data အတွက် [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ကိုအသုံးပြုနိုင်ပါတယ်။
browser API များစွာ ရှိပြီး၊ data သိမ်းဆည်းရန် အထူးစိတ်ဝင်စားစရာကောင်းသော API နှစ်ခု ရှိသည်-
Browser API များစွာရှိပြီး data ကိုသိမ်းဆည်းဖို့အတွက်အခြားရွေးချယ်မှုများလည်းရှိပါတယ်။ အထူးသဖြင့်အကျိုးရှိတဲ့ API နှစ်ခုရှိပါတယ်-
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) တစ်ခုဖြစ်ပြီး၊ လက်ရှိ website-specific data ကို session များအကြား ထိန်းသိမ်းထားနိုင်သည်။ သိမ်းဆည်းထားသော data သည် expiration မရှိပါ။
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ဒီ API သည် `localStorage` နှင့် တူသော်လည်း၊ session ပြီးဆုံးသည့်အခါ (browser ကို ပိတ်သည့်အခါ) data ကို ဖျက်သိမ်းသည်။
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) တစ်ခုဖြစ်ပြီး လက်ရှိ website အတွက် data ကို session များအကြားတည်ရှိနေစေပါတယ်။ ဒီ data က expiration မရှိပါဘူး။
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ဒီ API က `localStorage` နဲ့တူပေမယ့် session ပြီးဆုံးတဲ့အခါ (browser ကိုပိတ်တဲ့အခါ) data ကိုရှင်းလင်းပေးပါတယ်။
API နှစ်ခုလုံးသည် [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) ကိုသာ သိမ်းဆည်းနိုင်သည်။ complex object များကို သိမ်းဆည်းလိုပါက၊ [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) ကို အသုံးပြု၍ [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format သို့ serialize လုပ်ရန် လိုအပ်သည်။
API နှစ်ခုလုံးက [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) ကိုသာသိမ်းဆည်းနိုင်ပါတယ်။ Complex object တွေကိုသိမ်းဆည်းချင်ရင် [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format ကိုအသုံးပြုပြီး serialize လုပ်ဖို့လိုအပ်ပါတယ်၊ [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) ကိုအသုံးပြုပါ။
✅ server မရှိသော ဝက်ဘ်အက်ပ်တစ်ခု ဖန်တီးလိုပါက၊ [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) ကို အသုံးပြု၍ client တွင် database တစ်ခု ဖန်တီးနိုင်သည်။ ဒီ API သည် အဆင့်မြင့်အသုံးပြုမှုများ သို့မဟုတ် data အများကြီး သိမ်းဆည်းလိုပါက သင့်တော်သည်၊ အကြောင်းမှာ အသုံးပြုရန် ပိုမိုရှုပ်ထွေးသောကြောင့် ဖြစ်သည်။
✅ Server မရှိတဲ့ web app တစ်ခုကိုဖန်တီးချင်ရင်၊ [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) ကိုအသုံးပြုပြီး client မှာ database တစ်ခုဖန်တီးနိုင်ပါတယ်။ ဒါက advanced use case တွေအတွက်သို့မဟုတ် data အများကြီးသိမ်းဆည်းဖို့လိုအပ်တဲ့အခါအတွက်သာအသုံးပြုသင့်ပါတယ်၊ အလွန်ရှုပ်ထွေးတဲ့အတွက်။
ဒီနည်းလမ်းနဲ့ user account data ကတည်ရှိနေပြီး၊ state update အားလုံးကို centralized လုပ်ထားတဲ့အကျိုးကျေးဇူးကိုရရှိပါတယ်။ ဒီမှာတော့ အရင်ကလုပ်ထားတဲ့ refactor တွေကအကျိုးကျေးဇူးပေးလာ

# बैंकिङ एप निर्माण भाग ४: स्टेट म्यानेजमेन्टको अवधारणा
# बैंकिङ एप बनाउनुहोस् भाग ४: स्टेट म्यानेजमेन्टका अवधारणाहरू
## प्रि-लेक्चर क्विज
@ -15,15 +15,15 @@ CO_OP_TRANSLATOR_METADATA:
### परिचय
जसरी वेब एप्लिकेसन बढ्दै जान्छ, सबै डेटा प्रवाहलाई ट्र्याक गर्नु चुनौतीपूर्ण हुन्छ। कुन कोडले डेटा प्राप्त गर्छ, कुन पेजले यसलाई प्रयोग गर्छ, कहिले र कहाँ यसलाई अपडेट गर्नुपर्छ... यो सजिलै जटिल कोडमा परिणत हुन सक्छ जसलाई मर्मत गर्न गाह्रो हुन्छ। यो विशेष गरी सत्य हो जब तपाईंले आफ्नो एपका विभिन्न पेजहरूमा डेटा साझा गर्न आवश्यक हुन्छ, जस्तै प्रयोगकर्ता डेटा। *स्टेट म्यानेजमेन्ट* को अवधारणा सबै प्रकारका प्रोग्रामहरूमा सधैं रहेको छ, तर वेब एपहरू जटिलतामा बढ्दै जाँदा यो विकासको क्रममा सोच्नुपर्ने मुख्य बिन्दु बनेको छ।
जसरी वेब एप्लिकेसन ठूलो हुँदै जान्छ, सबै डेटा प्रवाहहरू ट्र्याक गर्न गाह्रो हुन्छ। कुन कोडले डेटा लिन्छ, कुन पृष्ठले यसलाई प्रयोग गर्छ, कहिले र कहाँ अपडेट गर्नुपर्छ... सजिलैसँग जटिल कोड बन्न सक्छ जसलाई मर्मत गर्न गाह्रो हुन्छ। यो विशेष गरी सत्य हो जब तपाईंले आफ्नो एपका विभिन्न पृष्ठहरूमा डेटा साझा गर्नुपर्छ, जस्तै प्रयोगकर्ता डेटा। *स्टेट म्यानेजमेन्ट* को अवधारणा सबै प्रकारका प्रोग्रामहरूमा सधैं अस्तित्वमा रहेको छ, तर वेब एपहरू जटिल हुँदै जाँदा यो विकासको क्रममा सोच्नुपर्ने मुख्य बिन्दु बनेको छ।
यस अन्तिम भागमा, हामीले बनाएको एपलाई पुनः हेर्नेछौं ताकि स्टेटलाई व्यवस्थापन गर्न सकियोस्, ब्राउजर रिफ्रेसलाई कुनै पनि समयमा समर्थन गर्न सकियोस्, र प्रयोगकर्ता सत्रहरूमा डेटा कायम राख्न सकियोस्।
यस अन्तिम भागमा, हामीले बनाएको एपलाई पुनः हेर्नेछौं र स्टेट कसरी व्यवस्थापन गरिन्छ भन्ने कुरा पुनर्विचार गर्नेछौं, जसले ब्राउजर रिफ्रेसलाई कुनै पनि समयमा समर्थन गर्न र प्रयोगकर्ता सत्रहरूमा डेटा कायम राख्न अनुमति दिन्छ।
### पूर्वापेक्षा
### पूर्वआवश्यकता
यस पाठको लागि तपाईंले वेब एपको [डेटा फेचिङ](../3-data/README.md) भाग पूरा गरिसक्नुपर्छ। तपाईंले [Node.js](https://nodejs.org) स्थापना गर्नुपर्छ र [सर्भर API](../api/README.md) स्थानीय रूपमा चलाउनुपर्छ ताकि तपाईं खाता डेटा व्यवस्थापन गर्न सक्नुहुन्छ।
यस पाठको लागि तपाईंले वेब एपको [डेटा फेचिङ](../3-data/README.md) भाग पूरा गरिसक्नुपर्छ। तपाईंले [Node.js](https://nodejs.org) स्थापना गर्नुपर्छ र [सर्भर एपीआई](../api/README.md) लाई स्थानीय रूपमा चलाउनुपर्छ ताकि तपाईं खाता डेटा व्यवस्थापन गर्न सक्नुहुन्छ।
तपाईंले सर्भर ठीकसँग चलिरहेको छ कि छैन भनेर जाँच गर्न टर्मिनलमा यो कमाण्ड चलाउन सक्नुहुन्छ:
तपाईंले यो कमाण्ड टर्मिनलमा चलाएर सर्भर ठीकसँग चलिरहेको छ कि छैन परीक्षण गर्न सक्नुहुन्छ:
```sh
curl http://localhost:5000/api
@ -32,36 +32,36 @@ curl http://localhost:5000/api
---
## स्टेट म्यानेजमेन्ट पुनःविचार गर्नुहोस्
## स्टेट म्यानेजमेन्ट पुनर्विचार गर्नुहोस्
[अघिल्लो पाठ](../3-data/README.md) मा, हामीले हाम्रो एपमा ग्लोबल `account` भेरिएबलको साथ स्टेटको आधारभूत अवधारणा प्रस्तुत गरेका थियौं, जसले हालको लगइन गरिएको प्रयोगकर्ताको बैंक डेटा समावेश गर्दछ। तर, हाम्रो हालको कार्यान्वयनमा केही कमजोरीहरू छन्। ड्यासबोर्डमा हुँदा पेज रिफ्रेस गर्नुहोस्। के हुन्छ?
[अघिल्लो पाठ](../3-data/README.md) मा, हामीले हाम्रो एपमा स्टेटको आधारभूत अवधारणा प्रस्तुत गर्यौं, जसमा `account` नामक ग्लोबल भेरिएबलले हाल लगइन गरिएको प्रयोगकर्ताको बैंक डेटा समावेश गर्दछ। तर, हाम्रो हालको कार्यान्वयनमा केही कमजोरीहरू छन्। ड्यासबोर्डमा हुँदा पृष्ठ रिफ्रेस गरेर प्रयास गर्नुहोस्। के हुन्छ?
- स्टेटलाई परिवर्तन गर्ने धेरै फङ्सनहरू छन्। एप बढ्दै जाँदा, यसले परिवर्तनहरू ट्र्याक गर्न गाह्रो बनाउँछ र एउटा अपडेट गर्न बिर्सन सजिलो हुन्छ।
- स्टेट सफा गरिँदैन, त्यसैले जब तपाईं *Logout* क्लिक गर्नुहुन्छ, खाता डेटा अझै त्यहाँ हुन्छ, यद्यपि तपाईं लगइन पेजमा हुनुहुन्छ।
- स्टेट कायम हुँदैन, किनभने ब्राउजर रिफ्रेस गर्दा तपाईंलाई लगइन पृष्ठमा फिर्ता लैजान्छ।
- स्टेट परिवर्तन गर्ने धेरै फङ्सनहरू छन्। एप ठूलो हुँदै जाँदा, यसले परिवर्तनहरू ट्र्याक गर्न गाह्रो बनाउँछ र कुनै एक अपडेट गर्न बिर्सन सजिलो हुन्छ।
- स्टेट सफा गरिँदैन, त्यसैले जब तपाईं *Logout* क्लिक गर्नुहुन्छ, खाता डेटा अझै त्यहाँ हुन्छ, यद्यपि तपाईं लगइन पृष्ठमा हुनुहुन्छ।
हामी यी समस्याहरूलाई एक-एक गरेर समाधान गर्न कोड अपडेट गर्न सक्थ्यौं, तर यसले कोड दोहोर्याउने र एपलाई अझ जटिल र मर्मत गर्न गाह्रो बनाउँछ। वा हामी केही मिनेट रोक्न सक्थ्यौं र हाम्रो रणनीति पुनः विचार गर्न सक्थ्यौं।
हामी यी समस्याहरूलाई एक-एक गरी समाधान गर्न कोड अपडेट गर्न सक्छौं, तर यसले कोड दोहोर्याउने र एपलाई अझ जटिल र मर्मत गर्न गाह्रो बनाउनेछ। वा हामी केही मिनेट रोक्न सक्छौं र हाम्रो रणनीतिलाई पुनर्विचार गर्न सक्छौं।
> यहाँ हामी वास्तवमा कुन समस्याहरू समाधान गर्न खोजिरहेका छौं?
[स्टेट म्यानेजमेन्ट](https://en.wikipedia.org/wiki/State_management) भनेको यी दुई विशेष समस्याहरू समाधान गर्न राम्रो दृष्टिकोण खोज्नु हो:
[स्टेट म्यानेजमेन्ट](https://en.wikipedia.org/wiki/State_management) भनेको यी दुई विशेष समस्याहरू समाधान गर्न राम्रो दृष्टिकोण फेला पार्नु हो:
- स्टेट डेटा सधैं प्रयोगकर्ता इन्टरफेससँग (र यसको उल्टो) कसरी समन्वयमा राख्ने?
यी समस्याहरू समाधान गरेपछि, तपाईंले सामना गर्न सक्ने अन्य समस्याहरू या त पहिले नै समाधान भएका हुन सक्छन् वा समाधान गर्न सजिलो भएका हुन सक्छन्। यी समस्याहरू समाधान गर्न धेरै सम्भावित दृष्टिकोणहरू छन्, तर हामी **डेटा र यसलाई परिवर्तन गर्ने तरिकाहरूलाई केन्द्रित गर्ने** सामान्य समाधानमा जानेछौं। डेटा प्रवाह यसरी जानेछ:
यी समस्याहरू समाधान गरेपछि, तपाईंले सामना गर्न सक्ने अन्य कुनै पनि समस्याहरू पहिले नै समाधान भइसकेका हुन सक्छन् वा समाधान गर्न सजिलो भएका हुन सक्छन्। यी समस्याहरू समाधान गर्न धेरै सम्भावित दृष्टिकोणहरू छन्, तर हामी **डेटा र यसलाई परिवर्तन गर्ने तरिकाहरूलाई केन्द्रीकृत गर्ने** सामान्य समाधान अपनाउनेछौं। डेटा प्रवाह यसरी जानेछ:


> यहाँ हामी डेटा स्वतः दृश्य अपडेट ट्रिगर गर्ने भाग कभर गर्ने छैनौं, किनकि यो [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming) को अधिक उन्नत अवधारणासँग सम्बन्धित छ। यदि तपाईं गहिरो अध्ययन गर्न इच्छुक हुनुहुन्छ भने यो राम्रो विषय हो।
> यहाँ हामी डेटा स्वचालित रूपमा भ्यू अपडेट ट्रिगर गर्ने भाग कभर गर्नेछैनौं, किनभने यो [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming) का थप उन्नत अवधारणाहरूमा आधारित छ। यदि तपाईं गहिरो अध्ययन गर्न इच्छुक हुनुहुन्छ भने यो राम्रो विषय हो।
✅ स्टेट म्यानेजमेन्टका लागि विभिन्न दृष्टिकोणहरू भएका धेरै लाइब्रेरीहरू छन्, [Redux](https://redux.js.org) एक लोकप्रिय विकल्प हो। यसमा प्रयोग गरिएका अवधारणाहरू र ढाँचाहरू हेर्नुहोस्, किनकि यो प्रायः ठूलो वेब एपहरूमा तपाईंले सामना गर्न सक्ने सम्भावित समस्याहरू र तिनीहरूलाई कसरी समाधान गर्न सकिन्छ भन्ने सिक्ने राम्रो तरिका हो।
✅ स्टेट म्यानेजमेन्टका लागि विभिन्न दृष्टिकोणहरू भएका धेरै पुस्तकालयहरू छन्, [Redux](https://redux.js.org) एक लोकप्रिय विकल्प हो। यसमा प्रयोग गरिएका अवधारणाहरू र ढाँचाहरू हेर्नुहोस्, किनभने यो ठूलो वेब एपहरूमा तपाईंले सामना गर्न सक्ने सम्भावित समस्याहरू र तिनीहरूलाई कसरी समाधान गर्न सकिन्छ भन्ने कुरा सिक्नको लागि प्रायः राम्रो तरिका हो।
### कार्य
हामी थोरै रिफ्याक्टरिङबाट सुरु गर्नेछौं। `account` घोषणा प्रतिस्थापन गर्नुहोस्:
हामी थोरै कोड पुनर्संरचना गरेर सुरु गर्नेछौं। `account` घोषणा प्रतिस्थापन गर्नुहोस्:
```js
let account = null;
@ -75,25 +75,25 @@ let state = {
};
```
विचार भनेको हाम्रो एप डेटा सबैलाई एकल स्टेट वस्तुमा *केन्द्रित* गर्नु हो। अहिले स्टेटमा हामीसँग `account` मात्र छ, त्यसैले यसले धेरै परिवर्तन गर्दैन, तर यसले विकासको लागि मार्ग बनाउँछ।
यो विचार हाम्रो सम्पूर्ण एप डेटा एकल स्टेट वस्तुमा *केन्द्रीकृत* गर्ने हो। अहिले स्टेटमा केवल `account` छ, त्यसैले यसले धेरै परिवर्तन गर्दैन, तर यसले भविष्यका विकासहरूको लागि बाटो खोल्छ।
हामी यसलाई प्रयोग गर्ने फङ्सनहरू पनि अपडेट गर्नुपर्छ। `register()` र `login()` फङ्सनहरूमा, `account = ...` लाई `state.account = ...` ले प्रतिस्थापन गर्नुहोस्;
हामीले यसलाई प्रयोग गर्ने फङ्सनहरू पनि अपडेट गर्नुपर्छ। `register()` र `login()` फङ्सनहरूमा, `account = ...` लाई `state.account = ...` ले प्रतिस्थापन गर्नुहोस्;
`updateDashboard()` फङ्सनको शीर्षमा, यो लाइन थप्नुहोस्:
`updateDashboard()` फङ्सनको सुरुमा, यो लाइन थप्नुहोस्:
```js
const account = state.account;
```
यो रिफ्याक्टरिङले आफैंमा धेरै सुधार ल्याएको छैन, तर विचार भनेको आगामी परिवर्तनहरूको लागि आधार तयार गर्नु हो।
यो पुनर्संरचनाले आफैंमा धेरै सुधार ल्याएन, तर विचार आगामी परिवर्तनहरूको लागि आधार तयार गर्नु थियो।
## डेटा परिवर्तन ट्र्याक गर्नुहोस्
## डेटा परिवर्तनहरू ट्र्याक गर्नुहोस्
अब हामीले हाम्रो डेटा भण्डारण गर्न `state` वस्तु राखेका छौं, अर्को चरण भनेको अपडेटहरू केन्द्रित गर्नु हो। उद्देश्य भनेको कुनै पनि परिवर्तनहरू र तिनीहरू कहिले हुन्छन् भनेर ट्र्याक गर्न सजिलो बनाउनु हो।
अब हामीले डेटा भण्डारण गर्न `state` वस्तु राखेका छौं, अर्को चरण भनेको अपडेटहरू केन्द्रीकृत गर्नु हो। उद्देश्य भनेको कुनै पनि परिवर्तनहरू र तिनीहरू कहिले हुन्छन् भन्ने कुरा ट्र्याक गर्न सजिलो बनाउनु हो।
`state` वस्तुमा परिवर्तनहरू गर्नबाट बच्न, यसलाई [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) मान्नु राम्रो अभ्यास हो, जसको अर्थ यसलाई बिल्कुलै परिवर्तन गर्न सकिँदैन। यसको मतलब तपाईंले यसमा केही परिवर्तन गर्न चाहनुहुन्छ भने नयाँ स्टेट वस्तु सिर्जना गर्नुपर्छ। यसो गर्दा, तपाईंले सम्भावित अवांछित [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) को बारेमा सुरक्षा निर्माण गर्नुहुन्छ, र तपाईंको एपमा नयाँ सुविधाहरू कार्यान्वयन गर्ने सम्भावनाहरू खोल्नुहुन्छ जस्तै undo/redo लागू गर्ने, साथै डिबग गर्न सजिलो बनाउने। उदाहरणका लागि, तपाईंले स्टेटमा गरिएका प्रत्येक परिवर्तनलाई लग गर्न सक्नुहुन्छ र बगको स्रोत बुझ्न परिवर्तनहरूको इतिहास राख्न सक्नुहुन्छ।
`state` वस्तुमा परिवर्तनहरू हुन नदिन, यसलाई [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) मान्नु राम्रो अभ्यास हो, जसको अर्थ यसलाई कुनै पनि रूपमा परिवर्तन गर्न सकिँदैन। यसको मतलब तपाईंले यसमा केही परिवर्तन गर्न चाहनुहुन्छ भने नयाँ स्टेट वस्तु सिर्जना गर्नुपर्छ। यसो गर्दा, तपाईंले सम्भावित अवाञ्छित [साइड इफेक्ट्स](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) बाट सुरक्षा निर्माण गर्नुहुन्छ, र तपाईंको एपमा नयाँ सुविधाहरू कार्यान्वयन गर्ने सम्भावनाहरू खोल्नुहुन्छ, जस्तै undo/redo कार्यान्वयन गर्नु, साथै डिबग गर्न सजिलो बनाउनु। उदाहरणका लागि, तपाईंले स्टेटमा गरिएका प्रत्येक परिवर्तनलाई लग गर्न सक्नुहुन्छ र बगको स्रोत बुझ्न परिवर्तनहरूको इतिहास राख्न सक्नुहुन्छ।
जाभास्क्रिप्टमा, [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) प्रयोग गरेर वस्तुको अपरिवर्तनीय संस्करण सिर्जना गर्न सकिन्छ। यदि तपाईंले अपरिवर्तनीय वस्तुमा परिवर्तन गर्न प्रयास गर्नुभयो भने, अपवाद उठाइनेछ।
जाभास्क्रिप्टमा, [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) प्रयोग गरेर वस्तुको अपरिवर्तनीय संस्करण सिर्जना गर्न सकिन्छ। यदि तपाईंले अपरिवर्तनीय वस्तुमा परिवर्तन गर्न प्रयास गर्नुभयो भने, अपवाद उत्पन्न हुनेछ।
✅ के तपाईंलाई *shallow* र *deep* अपरिवर्तनीय वस्तु बीचको भिन्नता थाहा छ? तपाईं यसबारे [यहाँ](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) पढ्न सक्नुहुन्छ।
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
यस फङ्सनमा, हामी नयाँ स्टेट वस्तु सिर्जना गर्दैछौं र [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) प्रयोग गरेर अघिल्लो स्टेटबाट डेटा प्रतिलिपि गर्दैछौं। त्यसपछि हामी [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` प्रयोग गरेर स्टेट वस्तुको विशेष सम्पत्तिलाई नयाँ डेटा संग अधिलेखन गर्दैछौं। अन्तमा, हामी `Object.freeze()` प्रयोग गरेर वस्तुलाई लक गर्दैछौं ताकि यसलाई परिवर्तन गर्न सकिँदैन। अहिले स्टेटमा `account` सम्पत्ति मात्र छ, तर यस दृष्टिकोणले तपाईंलाई स्टेटमा आवश्यक जति सम्पत्तिहरू थप्न अनुमति दिन्छ।
यस फङ्सनमा, हामी नयाँ स्टेट वस्तु सिर्जना गर्दैछौं र [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) प्रयोग गरेर अघिल्लो स्टेटबाट डेटा प्रतिलिपि गर्दैछौं। त्यसपछि हामी [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` प्रयोग गरेर स्टेट वस्तुको विशेष सम्पत्तिलाई नयाँ डेटा संग अधिलेखन गर्छौं। अन्तमा, हामी `Object.freeze()` प्रयोग गरेर वस्तुलाई लक गर्छौं ताकि यसलाई परिवर्तन गर्न सकिँदैन। अहिले स्टेटमा `account` सम्पत्ति मात्र भण्डारण गरिएको छ, तर यस दृष्टिकोणले तपाईंलाई स्टेटमा आवश्यक जति सम्पत्तिहरू थप्न अनुमति दिन्छ।
हामीले स्टेटको सुरुवात पनि अपडेट गर्नुपर्छ ताकि प्रारम्भिक स्टेट पनि फ्रिज गरिएको हो:
हामीले `state` को सुरुवात पनि अपडेट गर्नेछौं ताकि प्रारम्भिक स्टेट पनि फ्रिज गरिएको सुनिश्चित होस्:
`login` फङ्सनसँग पनि त्यस्तै गर्नुहोस्, `state.account = data;` लाई प्रतिस्थापन गर्नुहोस्:
`login` फङ्सनमा पनि त्यस्तै गर्नुहोस्, `state.account = data;` लाई प्रतिस्थापन गरेर:
```js
updateState('account', data);
@ -143,35 +143,35 @@ function logout() {
}
```
`updateDashboard()` मा, `return navigate('/login');`रिडिरेक्शनलाई `return logout();` ले प्रतिस्थापन गर्नुहोस्;
`updateDashboard()` मा, `return navigate('/login');`पुनर्निर्देशनलाई `return logout();` ले प्रतिस्थापन गर्नुहोस्;
नयाँ खाता दर्ता गर्ने, लगआउट गर्ने र फेरि लगइन गर्ने प्रयास गर्नुहोस् ताकि सबै कुरा अझै ठीकसँग काम गरिरहेको छ कि छैन जाँच गर्नुहोस्।
नयाँ खाता दर्ता गर्ने, लगआउट गर्ने र फेरि लगइन गर्ने प्रयास गर्नुहोस् ताकि सबै कुरा अझै ठीकसँग काम गरिरहेको छ कि छैन जाँच गर्नुहोस्।
> टिप: तपाईंले `updateState()` को तल `console.log(state)` थपेर र आफ्नो ब्राउजरको विकास उपकरणमा कन्सोल खोलेर सबै स्टेट परिवर्तनहरू हेर्न सक्नुहुन्छ।
> सुझाव: तपाईंले `updateState()` को तल `console.log(state)` थपेर र आफ्नो ब्राउजरको विकास उपकरणहरूमा कन्सोल खोलेर सबै स्टेट परिवर्तनहरू हेर्न सक्नुहुन्छ।
## स्टेट कायम राख्नुहोस्
धेरै वेब एपहरू सही रूपमा काम गर्न डेटा कायम राख्न आवश्यक हुन्छ। सबै महत्त्वपूर्ण डेटा सामान्यतया डेटाबेसमा भण्डारण गरिन्छ र सर्भर API मार्फत पहुँच गरिन्छ, जस्तै हाम्रो केसमा प्रयोगकर्ता खाता डेटा। तर कहिलेकाहीँ, ब्राउजरमा चलिरहेको क्लाइन्ट एपमा केही डेटा कायम राख्नु पनि रोचक हुन्छ, राम्रो प्रयोगकर्ता अनुभवको लागि वा लोडिङ प्रदर्शन सुधार गर्न।
धेरैजसो वेब एपहरू सही रूपमा काम गर्न डेटा कायम राख्न आवश्यक हुन्छ। सबै महत्त्वपूर्ण डेटा सामान्यतया डेटाबेसमा भण्डारण गरिन्छ र सर्भर एपीआई मार्फत पहुँच गरिन्छ, जस्तै हाम्रो केसमा प्रयोगकर्ता खाता डेटा। तर कहिलेकाहीँ, ब्राउजरमा चलिरहेको क्लाइन्ट एपमा केही डेटा कायम राख्नु पनि उपयोगी हुन्छ, राम्रो प्रयोगकर्ता अनुभवको लागि वा लोडिङ प्रदर्शन सुधार गर्न।
जब तपाईं आफ्नो ब्राउजरमा डेटा कायम राख्न चाहनुहुन्छ, केही महत्त्वपूर्ण प्रश्नहरू सोध्नुपर्छ:
जब तपाईं आफ्नो ब्राउजरमा डेटा कायम राख्न चाहनुहुन्छ, तपाईंले केही महत्त्वपूर्ण प्रश्नहरू सोध्नुपर्छ:
- *डेटा संवेदनशील छ?* तपाईंले क्लाइन्टमा कुनै पनि संवेदनशील डेटा भण्डारण गर्नबाट बच्नुपर्छ, जस्तै प्रयोगकर्ता पासवर्डहरू।
- *तपाईंलाई यो डेटा कति समयसम्म राख्न आवश्यक छ?* तपाईंले यो डेटा केवल वर्तमान सत्रको लागि पहुँच गर्न चाहनुहुन्छ वा यसलाई सधैंको लागि भण्डारण गर्न चाहनुहुन्छ?
- *डेटा संवेदनशील छ?* तपाईंले क्लाइन्टमा कुनै पनि संवेदनशील डेटा, जस्तै प्रयोगकर्ता पासवर्डहरू भण्डारण गर्नबाट बच्नुपर्छ।
- *तपाईंलाई यो डेटा कति समयसम्म राख्न आवश्यक छ?*के तपाईंले यो डेटा केवल हालको सत्रको लागि पहुँच गर्न चाहनुहुन्छ वा तपाईं यसलाई सधैंको लागि भण्डारण गर्न चाहनुहुन्छ?
वेब एप भित्र जानकारी भण्डारण गर्ने धेरै तरिकाहरू छन्, तपाईंले के हासिल गर्न चाहनुहुन्छ भन्नेमा निर्भर गर्दै। उदाहरणका लागि, तपाईंले खोज क्वेरी भण्डारण गर्न URL प्रयोग गर्न सक्नुहुन्छ, र यसलाई प्रयोगकर्ताहरू बीच साझा गर्न सक्नुहुन्छ। तपाईंले [HTTP कुकीहरू](https://developer.mozilla.org/docs/Web/HTTP/Cookies) प्रयोग गर्न सक्नुहुन्छ यदि डेटा सर्भरसँग साझा गर्न आवश्यक छ, जस्तै [प्रमाणीकरण](https://en.wikipedia.org/wiki/Authentication) जानकारी।
वेब एप भित्र जानकारी भण्डारण गर्ने धेरै तरिकाहरू छन्, तपाईंले के हासिल गर्न चाहनुहुन्छ भन्ने कुरामा निर्भर गर्दै। उदाहरणका लागि, तपाईंले URL हरू प्रयोग गरेर खोज क्वेरी भण्डारण गर्न सक्नुहुन्छ, र यसलाई प्रयोगकर्ताहरू बीच साझा गर्न सक्नुहुन्छ। तपाईंले [HTTP कुकीहरू](https://developer.mozilla.org/docs/Web/HTTP/Cookies) पनि प्रयोग गर्न सक्नुहुन्छ यदि डेटा सर्भरसँग साझा गर्न आवश्यक छ भने, जस्तै [प्रमाणीकरण](https://en.wikipedia.org/wiki/Authentication) जानकारी।
अर्को विकल्प भनेको डेटा भण्डारण गर्न ब्राउजर API मध्ये एक प्रयोग गर्नु हो। दुई विशेष रूपमा रोचक छन्:
अर्को विकल्प भनेको डेटा भण्डारण गर्न ब्राउजर एपीआईहरू मध्ये एक प्रयोग गर्नु हो। तीमध्ये दुई विशेष रूपमा चाखलाग्दो छन्:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): एक [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) जसले विभिन्न सत्रहरूमा वर्तमान वेबसाइटको लागि डेटा कायम राख्न अनुमति दिन्छ। यसमा भण्डारण गरिएको डेटा कहिल्यै समाप्त हुँदैन।
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): यो `localStorage` जस्तै काम गर्छ तर यसमा भण्डारण गरिएको डेटा सत्र समाप्त हुँदा (ब्राउजर बन्द हुँदा) सफा गरिन्छ।
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): एक [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) जसले विभिन्न सत्रहरूमा हालको वेबसाइटसँग विशेष डेटा कायम राख्न अनुमति दिन्छ। यसमा भण्डारण गरिएको डेटा कहिल्यै समाप्त हुँदैन।
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): यो `localStorage` जस्तै काम गर्छ तर यसमा भण्डारण गरिएको डेटा सत्र समाप्त हुँदा (ब्राउजर बन्द हुँदा) मेटिन्छ।
ध्यान दिनुहोस् कि यी दुवै API ले केवल [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) भण्डारण गर्न अनुमति दिन्छ। यदि तपाईंले जटिल वस्तुहरू भण्डारण गर्न चाहनुहुन्छ भने, तपाईंले यसलाई [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) प्रयोग गरेर [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) ढाँचामा सिरियलाइज गर्नुपर्नेछ।
ध्यान दिनुहोस् कि यी दुवै एपीआईहरूले केवल [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) भण्डारण गर्न अनुमति दिन्छ। यदि तपाईंले जटिल वस्तुहरू भण्डारण गर्न चाहनुहुन्छ भने, तपाईंले यसलाई [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) ढाँचामा सिरियलाइज गर्नुपर्नेछ [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) प्रयोग गरेर।
✅ यदि तपाईं सर्भर बिना काम गर्ने वेब एप बनाउन चाहनुहुन्छ भने, [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) प्रयोग गरेर क्लाइन्टमा डेटाबेस सिर्जना गर्न पनि सम्भव छ। यो उन्नत प्रयोगका लागि आरक्षित छ वा यदि तपाईंले महत्वपूर्ण मात्रामा डेटा भण्डारण गर्न आवश्यक छ भने, किनकि यो प्रयोग गर्न अधिक जटिल छ।
✅ यदि तपाईं सर्भर बिना काम गर्ने वेब एप बनाउन चाहनुहुन्छ भने, यो पनि सम्भव छ कि क्लाइन्टमा डेटाबेस सिर्जना गर्न [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) प्रयोग गर्नुहोस्। यो उन्नत प्रयोगका लागि वा यदि तपाईंलाई ठूलो मात्रामा डेटा भण्डारण गर्न आवश्यक छ भने आरक्षित छ, किनभने यो प्रयोग गर्न अधिक जटिल छ।
### कार्य
हामी चाहन्छौं कि हाम्रो प्रयोगकर्ताहरू स्पष्ट रूपमा *Logout* बटन क्लिक नगरेसम्म लगइन रहून्, त्यसैले हामी `localStorage` प्रयोग गरेर खाता डेटा भण्डारण गर्नेछौं। पहिलो, हामीले डेटा भण्डारण गर्न प्रयोग गर्ने कुञ्जी परिभाषित गरौं।
हामी चाहन्छौं कि हाम्रो प्रयोगकर्ताहरू *Logout* बटनमा स्पष्ट रूपमा क्लिक नगरेसम्म लगइन रहून्, त्यसैले हामी `localStorage` प्रयोग गरेर खाता डेटा भण्डारण गर्नेछौं। पहिलो, हामीले हाम्रो डेटा भण्डारण गर्न प्रयोग गर्ने कुञ्जी परिभाषित गरौं।
यससँग, प्रयोगकर्ता खाता डेटा कायम रहनेछ र हामीले पहिले केन्द्रित गरेका सबै स्टेट अपडेटहरू जस्तै सधैं अद्यावधिक हुनेछ। यहीँबाट हामीले गरेका सबै रिफ्याक्टरिङको फाइदा लिन सुरु गर्छौं 🙂।
यससँग, प्रयोगकर्ता खाता डेटा कायम हुनेछ र सबै समय अद्यावधिक हुनेछ, किनभने हामीले पहिले नै हाम्रो सबै स्टेट अपडेटहरू केन्द्रीकृत गरेका छौं। यहीँबाट हामीले गरेका सबै पुनर्संरचनाहरूको फाइदा लिन सुरु गर्छौं 🙂।
जसरी डेटा बचत गरिन्छ, हामीले एप लोड हुँदा यसलाई पुनःस्थापित गर्न पनि ध्यान दिनुपर्छ। किनकि हामीले अधिक सुरुवात कोड राख्न थाल्छौं, नयाँ `init` फङ्सन सिर्जना गर्नु राम्रो विचार हुन सक्छ, जसले `app.js` को तलको हाम्रो अघिल्लो कोड पनि समावेश गर्दछ:
डेटा भण्डारण गरिएको छ, त्यसैले एप लोड हुँदा यसलाई पुनःस्थापना गर्न पनि ध्यान दिनुपर्छ। किनभने हामीसँग अब धेरै सुरुवात कोड हुनेछ, नयाँ `init` फङ्सन सिर्जना गर्नु राम्रो विचार हुन सक्छ, जसले `app.js` को तलको हाम्रो अघिल्लो कोड पनि समावेश गर्दछ:
```js
function init() {
@ -202,11 +202,11 @@ function init() {
init();
```
यहाँ हामीले बचत गरिएको डेटा पुनः प्राप्त गर्छौं, र यदि त्यहाँ कुनै छ भने हामी स्टेटलाई त्यस अनुसार अपडेट गर्छौं। यो पेज अपडेटको क्रममा स्टेटमा निर्भर कोड हुन सक्ने भएकाले*रूट अपडेट गर्नु अघि* गर्न महत्त्वपूर्ण छ।
यहाँ हामीले भण्डारण गरिएको डेटा पुनःप्राप्त गर्छौं, र यदि कुनै छ भने हामी स्टेटलाई त्यस अनुसार अद्यावधिक गर्छौं। यो पृष्ठ अद्यावधिकको क्रममा स्टेटमा निर्भर कोड हुन सक्छ, त्यसैले यो*रूट अपडेट गर्नु अघि* गर्न महत्त्वपूर्ण छ।
हामीले *Dashboard* पेजलाई हाम्रो एपको डिफल्ट पेज बनाउन पनि सक्दछौं, किनकि हामी अब खाता डेटा कायम राख्दैछौं। यदि कुनै डेटा फेला परेन भने, ड्यासबोर्डले *Login* पेजमा रिडिरेक्ट गर्ने काम गर्छ। `updateRoute()` मा, फलब्याक `return navigate('/login');` लाई `return navigate('/dashboard');` ले प्रतिस्थापन गर्नुहोस्।
हामी*Dashboard* पृष्ठलाई हाम्रो एपको डिफल्ट पृष्ठ बनाउन सक्छौं, किनभने अब हामी खाता डेटा कायम राख्दैछौं। यदि कुनै डेटा फेला परेन भने, ड्यासबोर्डले *Login* पृष्ठमा पुनर्निर्देशनको ख्याल राख्छ। `updateRoute()` मा, फलब्याक `return navigate('/login');` लाई `return navigate('/dashboard');` ले प्रतिस्थापन गर्नुहोस्।
अब एपमा लगइन गर्नुहोस् र पेज रिफ्रेस गर्ने प्रयास गर्नुहोस्। तपाईं ड्यासबोर्डमा रहनुहुन्छ। यस अपडेटसँगै हामीले हाम्रो प्रारम्भिक समस्याहरूको ख्याल गरेका छौं...
अब एपमा लगइन गर्नुहोस् र पृष्ठ रिफ्रेस गर्ने प्रयास गर्नुहोस्। तपाईं ड्यासबोर्डमा रहनु पर्छ। यस अद्यावधिकसँग, हामीले हाम्रो सबै प्रारम्भिक समस्याहरू समाधान गरेका छौं...
अब ब्राउजरमा ड्यासबोर्ड पेज रिफ्रेस गर्ने प्रयास गर्नुहोस्। के हुन्छ? के तपाईंले नयाँ ट्रान्जेक्सन देख्नुभयो?
अब आफ्नो ब्राउजरमा ड्यासबोर्ड पृष्ठ रिफ्रेस गर्ने प्रयास गर्नुहोस्। के हुन्छ? के तपाईंले नयाँ ट्रान्जेक्सन देख्नुभयो?
स्टेट `localStorage` को कारण अनिश्चितकालसम्म कायम रहन्छ, तर यसको मतलब यो कहिल्यै अपडेट हुँदैन जबसम्म तपाईं एपबाट लगआउट गरेर फेरि लगइन गर्नुहुन्न!
स्टेट `localStorage` को कारण अनिश्चितकालसम्म कायम रहन्छ, तर यसको मतलब यो कहिल्यै अद्यावधिक हुँदैन जबसम्म तपाईं एपबाट लगआउट गरेर फेरि लगइन गर्नुहुन्न!
यसलाई समाधान गर्ने सम्भावित रणनीति भनेको ड्यासबोर्ड लोड हुँदा हरेक पटक खाता डेटा पुनः लोड गर्नु हो, ताकि पुरानो डेटा नहोस्।
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
यो मेथडले जाँच गर्छ कि हामी हाल लगइन गरिरहेका छौं र त्यसपछि सर्भरबाट खाता डेटा पुनः लोड गर्छ।
यस विधिले जाँच गर्छ कि हामी हाल लगइन गरिरहेका छौं र त्यसपछि सर्भरबाट खाता डेटा पुनः लोड गर्छ।
`refresh` नामक अर्को फङ्सन सिर्जना गर्नुहोस्:
@ -258,7 +258,7 @@ async function refresh() {
}
```
यो खाता डेटा अपडेट गर्छ, त्यसपछि ड्यासबोर्ड पेजको HTML अपडेट गर्ने काम गर्छ। यो ड्यासबोर्ड रूट लोड हुँदा हामीले कल गर्न आवश्यक छ। रूट परिभाषा अपडेट गर्नुहोस्:
यसले खाता डेटा अद्यावधिक गर्छ, त्यसपछि ड्यासबोर्ड पृष्ठको HTML अद्यावधिक गर्ने ख्याल राख्छ। यो ड्यासबोर्ड रूट लोड हुँदा हामीले कल गर्नुपर्ने कुरा हो। रूट परिभाषालाई यससँग अद्यावधिक गर्नुहोस्:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
अब ड्यासबोर्ड रिफ्रेस गर्ने प्रयास गर्नुहोस्, यसले अपडेट गरिएको खाता डेटा देखाउनु पर्छ।
अब ड्यासबोर्ड रिफ्रेस गर्ने प्रयास गर्नुहोस्, यसले अद्यावधिक गरिएको खाता डेटा देखाउनु पर्छ।
---
## 🚀 चुनौती
अब हामीले ड्यासबोर्ड लोड हुँदा हरेक पटक खाता डेटा पुनः लोड गर्छौं, के तपाईं सोच्नुहुन्छ कि हामीले *सबै खाता* डेटा कायम राख्न अझै आवश्यक छ?
अब हामीले ड्यासबोर्ड लोड हुँदा हरेक पटक खाता डेटा पुनः लोड गर्छौं, के तपाईंलाई लाग्छ कि हामीले *सबै खाता* डेटा कायम राख्न अझै आवश्यक छ?
`localStorage` बाट बचत र लोड गरिने कुरालाई एप काम गर्न आवश्यक पर्ने कुरामा मात्र सीमित गर्न प्रयास गर्नुहोस्।
`localStorage` बाट केवल एप काम गर्नको लागि पूर्ण रूपमा आवश्यक पर्ने कुरा मात्र भण्डारण र लोड गर्न परिवर्तन गर्न मिलेर काम गर्ने प्रयास गर्नुहोस्।
असाइनमेन्ट पूरा गरेपछि प्राप्त हुने उदाहरण परिणाम यहाँ छ:


---
**अस्वीकरण**:
यो दस्तावेज़ AI अनुवाद सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) प्रयोग गरेर अनुवाद गरिएको छ। हामी यथार्थताको लागि प्रयास गर्छौं, तर कृपया ध्यान दिनुहोस् कि स्वचालित अनुवादमा त्रुटिहरू वा अशुद्धताहरू हुन सक्छ। यसको मूल भाषा मा रहेको मूल दस्तावेज़लाई आधिकारिक स्रोत मानिनुपर्छ। महत्वपूर्ण जानकारीको लागि, व्यावसायिक मानव अनुवाद सिफारिस गरिन्छ। यस अनुवादको प्रयोगबाट उत्पन्न हुने कुनै पनि गलतफहमी वा गलत व्याख्याको लागि हामी जिम्मेवार हुनेछैनौं।
यो दस्तावेज़ AI अनुवाद सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) प्रयोग गरेर अनुवाद गरिएको हो। हामी यथार्थताको लागि प्रयास गर्छौं, तर कृपया ध्यान दिनुहोस् कि स्वचालित अनुवादमा त्रुटिहरू वा अशुद्धताहरू हुन सक्छ। यसको मूल भाषा मा रहेको मूल दस्तावेज़लाई आधिकारिक स्रोत मानिनुपर्छ। महत्वपूर्ण जानकारीको लागि, व्यावसायिक मानव अनुवाद सिफारिस गरिन्छ। यस अनुवादको प्रयोगबाट उत्पन्न हुने कुनै पनि गलतफहमी वा गलत व्याख्याको लागि हामी जिम्मेवार हुनेछैनौं।
Naarmate een webapplicatie groeit, wordt het steeds moeilijker om alle datastromen bij te houden. Welke code haalt de data op, welke pagina gebruikt het, waar en wanneer moet het worden bijgewerkt... Het is makkelijk om te eindigen met rommelige code die moeilijk te onderhouden is. Dit geldt vooral wanneer je data moet delen tussen verschillende pagina's van je app, zoals gebruikersgegevens. Het concept van *state management* heeft altijd bestaan in allerlei soorten programma's, maar nu webapps steeds complexer worden, is het een belangrijk punt om over na te denken tijdens de ontwikkeling.
Naarmate een webapplicatie groeit, wordt het steeds moeilijker om alle datastromen bij te houden. Welke code haalt de data op, welke pagina gebruikt het, waar en wanneer moet het worden bijgewerkt... Het is gemakkelijk om te eindigen met rommelige code die moeilijk te onderhouden is. Dit geldt vooral wanneer je gegevens moet delen tussen verschillende pagina's van je app, zoals gebruikersgegevens. Het concept van *state management* heeft altijd bestaan in allerlei soorten programma's, maar nu webapps steeds complexer worden, is het een belangrijk punt om over na te denken tijdens de ontwikkeling.
In dit laatste deel bekijken we de app die we hebben gebouwd opnieuw om te heroverwegen hoe de state wordt beheerd. Dit maakt ondersteuning voor browserverversingen op elk moment mogelijk en zorgt ervoor dat gegevens behouden blijven tussen gebruikerssessies.
In dit laatste deel bekijken we de app die we hebben gebouwd om opnieuw na te denken over hoe de state wordt beheerd, zodat ondersteuning voor browserverversing op elk moment mogelijk is en gegevens behouden blijven tussen gebruikerssessies.
### Vereisten
Je moet het [data ophalen](../3-data/README.md) deel van de webapp hebben voltooid voor deze les. Je moet ook [Node.js](https://nodejs.org) installeren en de [server-API](../api/README.md) lokaal uitvoeren, zodat je accountgegevens kunt beheren.
Je moet het [data ophalen](../3-data/README.md) deel van de webapp hebben voltooid voor deze les. Je moet ook [Node.js](https://nodejs.org) installeren en [de server-API](../api/README.md) lokaal uitvoeren, zodat je accountgegevens kunt beheren.
Je kunt controleren of de server correct werkt door dit commando in een terminal uit te voeren:
Je kunt testen of de server correct werkt door dit commando in een terminal uit te voeren:
```sh
curl http://localhost:5000/api
@ -32,17 +32,17 @@ curl http://localhost:5000/api
---
## State management heroverwegen
## Herziening van state management
In de [vorige les](../3-data/README.md) hebben we een basisconcept van state in onze app geïntroduceerd met de globale `account`-variabele die de bankgegevens bevat van de momenteel ingelogde gebruiker. Onze huidige implementatie heeft echter enkele tekortkomingen. Probeer de pagina te verversen terwijl je op het dashboard bent. Wat gebeurt er?
In de [vorige les](../3-data/README.md) introduceerden we een basisconcept van state in onze app met de globale `account`-variabele die de bankgegevens bevat van de momenteel ingelogde gebruiker. Onze huidige implementatie heeft echter enkele tekortkomingen. Probeer de pagina te verversen terwijl je op het dashboard bent. Wat gebeurt er?
Er zijn drie problemen met de huidige code:
- De state wordt niet behouden, want een browserverversing brengt je terug naar de inlogpagina.
- Er zijn meerdere functies die de state wijzigen. Naarmate de app groeit, kan dit het moeilijk maken om de wijzigingen bij te houden en is het makkelijk om te vergeten iets bij te werken.
- De state wordt niet behouden, omdat een browserverversing je terugbrengt naar de inlogpagina.
- Er zijn meerdere functies die de state wijzigen. Naarmate de app groeit, kan dit het moeilijk maken om de wijzigingen bij te houden en is het gemakkelijk om te vergeten een update uit te voeren.
- De state wordt niet opgeruimd, dus wanneer je op *Uitloggen* klikt, blijven de accountgegevens aanwezig, zelfs als je op de inlogpagina bent.
We zouden onze code kunnen bijwerken om deze problemen één voor één aan te pakken, maar dat zou leiden tot meer code duplicatie en de app complexer en moeilijker te onderhouden maken. Of we kunnen even pauzeren en onze strategie heroverwegen.
We zouden onze code kunnen bijwerken om deze problemen één voor één aan te pakken, maar dat zou meer code duplicatie creëren en de app complexer en moeilijker te onderhouden maken. Of we kunnen een paar minuten pauzeren en onze strategie heroverwegen.
> Welke problemen proberen we hier echt op te lossen?
@ -51,17 +51,17 @@ We zouden onze code kunnen bijwerken om deze problemen één voor één aan te p
- Hoe houden we de datastromen in een app begrijpelijk?
- Hoe zorgen we ervoor dat de state data altijd synchroon loopt met de gebruikersinterface (en vice versa)?
Als je deze problemen hebt aangepakt, zijn andere problemen die je mogelijk hebt, of al opgelost, of makkelijker op te lossen. Er zijn veel mogelijke benaderingen om deze problemen op te lossen, maar wij kiezen voor een veelgebruikte oplossing die bestaat uit **het centraliseren van de data en de manieren om deze te wijzigen**. De datastromen zouden er als volgt uitzien:
Als je deze hebt aangepakt, zijn eventuele andere problemen die je hebt mogelijk al opgelost of gemakkelijker op te lossen. Er zijn veel mogelijke benaderingen om deze problemen op te lossen, maar we kiezen voor een veelgebruikte oplossing die bestaat uit **het centraliseren van de data en de manieren om deze te wijzigen**. De datastromen zouden er als volgt uitzien:


> We behandelen hier niet het deel waarin de data automatisch de weergave bijwerkt, omdat dit gekoppeld is aan meer geavanceerde concepten van [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). Dit is een goed vervolgonderwerp als je een diepere duik wilt nemen.
> We behandelen hier niet het deel waarin de data automatisch de weergave-update triggert, omdat dit gekoppeld is aan meer geavanceerde concepten van [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). Dit is een goed onderwerp om verder in te duiken als je er klaar voor bent.
✅ Er zijn veel bibliotheken met verschillende benaderingen voor state management, waarbij [Redux](https://redux.js.org) een populaire optie is. Bekijk de concepten en patronen die worden gebruikt, omdat dit vaak een goede manier is om te leren welke potentiële problemen je kunt tegenkomen in grote webapps en hoe deze kunnen worden opgelost.
### Taak
We beginnen met een beetje refactoring. Vervang de `account`-declaratie:
We beginnen met een beetje refactoring. Vervang de `account`-verklaring:
```js
let account = null;
@ -75,31 +75,31 @@ let state = {
};
```
Het idee is om *alle app-data te centraliseren* in een enkel state-object. Voor nu hebben we alleen `account` in de state, dus het verandert niet veel, maar het creëert een basis voor toekomstige uitbreidingen.
Het idee is om *alle app-data te centraliseren* in een enkel state-object. We hebben momenteel alleen `account` in de state, dus het verandert niet veel, maar het creëert een pad voor toekomstige uitbreidingen.
We moeten ook de functies die het gebruiken bijwerken. Vervang in de functies `register()` en `login()``account = ...` door `state.account = ...`;
We moeten ook de functies bijwerken die het gebruiken. Vervang in de `register()`- en `login()`-functies`account = ...` door `state.account = ...`;
Voeg aan het begin van de functie `updateDashboard()` deze regel toe:
Voeg aan het begin van de `updateDashboard()`-functie deze regel toe:
```js
const account = state.account;
```
Deze refactoring op zichzelf brengt niet veel verbeteringen, maar het idee was om de basis te leggen voor de volgende wijzigingen.
Deze refactoring op zichzelf heeft niet veel verbeteringen gebracht, maar het idee was om de basis te leggen voor de volgende wijzigingen.
## Gegevenswijzigingen bijhouden
Nu we het `state`-object hebben geïntroduceerd om onze data op te slaan, is de volgende stap om de updates te centraliseren. Het doel is om het makkelijker te maken om wijzigingen en wanneer ze plaatsvinden bij te houden.
Nu we het `state`-object hebben geïntroduceerd om onze gegevens op te slaan, is de volgende stap om de updates te centraliseren. Het doel is om het gemakkelijker te maken om eventuele wijzigingen en wanneer ze plaatsvinden bij te houden.
Om te voorkomen dat wijzigingen worden aangebracht in het `state`-object, is het ook een goede gewoonte om het te beschouwen als [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), wat betekent dat het helemaal niet kan worden gewijzigd. Dit betekent ook dat je een nieuw state-object moet maken als je iets wilt wijzigen. Door dit te doen, bouw je een bescherming tegen mogelijk ongewenste [bijwerkingen](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) en open je mogelijkheden voor nieuwe functies in je app, zoals het implementeren van ongedaan maken/herhalen, terwijl het ook makkelijker wordt om te debuggen. Bijvoorbeeld, je zou elke wijziging in de state kunnen loggen en een geschiedenis van de wijzigingen kunnen bijhouden om de bron van een bug te begrijpen.
Om te voorkomen dat wijzigingen worden aangebracht in het `state`-object, is het ook een goede gewoonte om het te beschouwen als [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), wat betekent dat het helemaal niet kan worden gewijzigd. Dit betekent ook dat je een nieuw state-object moet maken als je iets wilt wijzigen. Door dit te doen, bouw je een bescherming tegen mogelijk ongewenste [bijwerkingen](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) en open je mogelijkheden voor nieuwe functies in je app, zoals het implementeren van undo/redo, terwijl het ook gemakkelijker wordt om te debuggen. Bijvoorbeeld, je zou elke wijziging in de state kunnen loggen en een geschiedenis van de wijzigingen kunnen bijhouden om de bron van een bug te begrijpen.
In JavaScript kun je [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) gebruiken om een onveranderlijke versie van een object te maken. Als je probeert wijzigingen aan te brengen in een onveranderlijk object, wordt er een uitzondering gegenereerd.
In JavaScript kun je [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) gebruiken om een immutable versie van een object te maken. Als je probeert wijzigingen aan te brengen in een immutable object, wordt er een uitzondering gegenereerd.
✅ Weet je het verschil tussen een *oppervlakkig* en een *diep* onveranderlijk object? Je kunt erover lezen [hier](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Ken je het verschil tussen een *shallow* en een *deep* immutable object? Je kunt er [hier](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) meer over lezen.
### Taak
Laten we een nieuwe functie `updateState()` maken:
Laten we een nieuwe `updateState()`-functie maken:
```js
function updateState(property, newData) {
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
In deze functie maken we een nieuw state-object en kopiëren we data van de vorige state met behulp van de [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Vervolgens overschrijven we een specifieke eigenschap van het state-object met de nieuwe data met behulp van de [bracket notatie](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` voor toewijzing. Tot slot vergrendelen we het object om wijzigingen te voorkomen met `Object.freeze()`. Voor nu hebben we alleen de `account`-eigenschap opgeslagen in de state, maar met deze aanpak kun je zoveel eigenschappen toevoegen als je nodig hebt.
In deze functie maken we een nieuw state-object en kopiëren we gegevens van de vorige state met behulp van de [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Vervolgens overschrijven we een specifieke eigenschap van het state-object met de nieuwe gegevens met behulp van de [bracket notatie](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` voor toewijzing. Ten slotte vergrendelen we het object om wijzigingen te voorkomen met `Object.freeze()`. We hebben momenteel alleen de `account`-eigenschap opgeslagen in de state, maar met deze aanpak kun je zoveel eigenschappen toevoegen als je nodig hebt.
We zullen ook de `state`-initialisatie bijwerken om ervoor te zorgen dat de initiële state ook bevroren is:
@ -132,7 +132,7 @@ Doe hetzelfde met de `login`-functie, vervang `state.account = data;` door:
updateState('account', data);
```
We grijpen nu de kans om het probleem op te lossen waarbij accountgegevens niet worden gewist wanneer de gebruiker op *Uitloggen* klikt.
We nemen nu de kans om het probleem op te lossen waarbij accountgegevens niet worden gewist wanneer de gebruiker op *Uitloggen* klikt.
Maak een nieuwe functie `logout()`:
@ -143,31 +143,31 @@ function logout() {
}
```
Vervang in `updateDashboard()` de omleiding `return navigate('/login');` door `return logout();`
Vervang in `updateDashboard()` de omleiding `return navigate('/login');` door `return logout()`;
Probeer een nieuw account te registreren, uit te loggen en opnieuw in te loggen om te controleren of alles nog steeds correct werkt.
> Tip: je kunt alle state-wijzigingen bekijken door `console.log(state)` toe te voegen onderaan `updateState()` en de console in de ontwikkelaarstools van je browser te openen.
> Tip: je kunt alle state-wijzigingen bekijken door `console.log(state)` toe te voegen onderaan `updateState()` en de console te openen in de ontwikkeltools van je browser.
## De state behouden
De meeste webapps moeten gegevens behouden om correct te kunnen werken. Alle kritieke gegevens worden meestal opgeslagen in een database en benaderd via een server-API, zoals de gebruikersaccountgegevens in ons geval. Maar soms is het ook interessant om bepaalde gegevens op te slaan in de client-app die in je browser draait, voor een betere gebruikerservaring of om de laadtijd te verbeteren.
De meeste webapps moeten gegevens behouden om correct te kunnen werken. Alle kritieke gegevens worden meestal opgeslagen in een database en benaderd via een server-API, zoals de gebruikersaccountgegevens in ons geval. Maar soms is het ook interessant om enkele gegevens te behouden in de client-app die in je browser draait, voor een betere gebruikerservaring of om de laadsnelheid te verbeteren.
Wanneer je gegevens in je browser wilt opslaan, zijn er een paar belangrijke vragen die je jezelf moet stellen:
Wanneer je gegevens wilt behouden in je browser, zijn er een paar belangrijke vragen die je jezelf moet stellen:
- *Zijn de gegevens gevoelig?* Je moet vermijden om gevoelige gegevens op de client op te slaan, zoals gebruikerswachtwoorden.
- *Hoe lang moet je deze gegevens bewaren?* Wil je deze gegevens alleen voor de huidige sessie gebruiken of wil je dat ze voor altijd worden opgeslagen?
Er zijn meerdere manieren om informatie op te slaan in een webapp, afhankelijk van wat je wilt bereiken. Bijvoorbeeld, je kunt de URL's gebruiken om een zoekopdracht op te slaan en deze deelbaar te maken tussen gebruikers. Je kunt ook [HTTP-cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) gebruiken als de gegevens moeten worden gedeeld met de server, zoals [authenticatie](https://en.wikipedia.org/wiki/Authentication) informatie.
Er zijn meerdere manieren om informatie binnen een webapp op te slaan, afhankelijk van wat je wilt bereiken. Bijvoorbeeld, je kunt de URL's gebruiken om een zoekopdracht op te slaan en deze deelbaar te maken tussen gebruikers. Je kunt ook [HTTP-cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) gebruiken als de gegevens moeten worden gedeeld met de server, zoals [authenticatie](https://en.wikipedia.org/wiki/Authentication) informatie.
Een andere optie is om een van de vele browser-API's te gebruiken voor het opslaan van gegevens. Twee daarvan zijn bijzonder interessant:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): een [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) waarmee je gegevens specifiek voor de huidige website kunt behouden over verschillende sessies. De opgeslagen gegevens verlopen nooit.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): dit werkt hetzelfde als `localStorage`, behalve dat de opgeslagen gegevens worden gewist wanneer de sessie eindigt (wanneer de browser wordt gesloten).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): een [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) waarmee gegevens specifiek voor de huidige website kunnen worden behouden tussen verschillende sessies. De opgeslagen gegevens verlopen nooit.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): deze werkt hetzelfde als `localStorage`, behalve dat de opgeslagen gegevens worden gewist wanneer de sessie eindigt (wanneer de browser wordt gesloten).
Merk op dat beide API's alleen [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) kunnen opslaan. Als je complexe objecten wilt opslaan, moet je ze serialiseren naar het [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-formaat met [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Let op dat beide API's alleen [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) kunnen opslaan. Als je complexe objecten wilt opslaan, moet je ze serialiseren naar het [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-formaat met [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Als je een webapp wilt maken die niet met een server werkt, is het ook mogelijk om een database op de client te maken met behulp van de [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Dit is gereserveerd voor geavanceerde gebruiksscenario's of als je een aanzienlijke hoeveelheid gegevens moet opslaan, omdat het complexer is om te gebruiken.
✅ Als je een webapp wilt maken die niet met een server werkt, is het ook mogelijk om een database op de client te maken met de [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Deze is gereserveerd voor geavanceerde gebruiksscenario's of als je een aanzienlijke hoeveelheid gegevens moet opslaan, omdat het complexer is om te gebruiken.
### Taak
@ -183,9 +183,9 @@ Voeg vervolgens deze regel toe aan het einde van de `updateState()`-functie:
Hiermee worden de gebruikersaccountgegevens opgeslagen en altijd up-to-date gehouden, omdat we eerder alle state-updates hebben gecentraliseerd. Dit is waar we beginnen te profiteren van al onze eerdere refactors 🙂.
Hiermee worden de gebruikersaccountgegevens behouden en altijd up-to-date gehouden, omdat we eerder alle state-updates hebben gecentraliseerd. Dit is waar we beginnen te profiteren van al onze eerdere refactors 🙂.
Omdat de gegevens worden opgeslagen, moeten we ook zorgen voor het herstellen ervan wanneer de app wordt geladen. Aangezien we meer initialisatiecode beginnen te krijgen, is het misschien een goed idee om een nieuwe `init`-functie te maken, die ook onze eerdere code onderaan `app.js` bevat:
Omdat de gegevens worden opgeslagen, moeten we ook zorgen voor het herstellen ervan wanneer de app wordt geladen. Aangezien we meer initialisatiecode beginnen te krijgen, kan het een goed idee zijn om een nieuwe `init`-functie te maken, die ook onze eerdere code onderaan `app.js` bevat:
```js
function init() {
@ -202,11 +202,11 @@ function init() {
init();
```
Hier halen we de opgeslagen gegevens op, en als er gegevens zijn, werken we de state dienovereenkomstig bij. Het is belangrijk om dit *voordat* de route wordt bijgewerkt te doen, omdat er mogelijk code is die afhankelijk is van de state tijdens de pagina-update.
Hier halen we de opgeslagen gegevens op, en als er gegevens zijn, werken we de state dienovereenkomstig bij. Het is belangrijk om dit *voor* het bijwerken van de route te doen, omdat er mogelijk code is die afhankelijk is van de state tijdens de pagina-update.
We kunnen ook de *Dashboard*-pagina de standaardpagina van onze applicatie maken, aangezien we nu de accountgegevens behouden. Als er geen gegevens worden gevonden, zorgt het dashboard er toch voor dat je wordt doorgestuurd naar de *Inlog*-pagina. Vervang in `updateRoute()` de fallback `return navigate('/login');` door `return navigate('/dashboard');`.
We kunnen ook de *Dashboard*-pagina de standaardpagina van onze applicatie maken, omdat we nu de accountgegevens behouden. Als er geen gegevens worden gevonden, zorgt het dashboard ervoor dat het doorverwijst naar de *Inloggen*-pagina. Vervang in `updateRoute()` de fallback `return navigate('/login');` door `return navigate('/dashboard');`.
Log nu in op de app en probeer de pagina te verversen. Je zou op het dashboard moeten blijven. Met die update hebben we al onze initiële problemen opgelost...
Log nu in op de app en probeer de pagina te verversen. Je zou op het dashboard moeten blijven. Met die update hebben we alle oorspronkelijke problemen opgelost...
Probeer nu de dashboardpagina in de browser te verversen. Wat gebeurt er? Zie je de nieuwe transactie?
Probeer nu je dashboardpagina in de browser te verversen. Wat gebeurt er? Zie je de nieuwe transactie?
De state wordt dankzij de `localStorage` voor onbepaalde tijd behouden, maar dat betekent ook dat deze nooit wordt bijgewerkt totdat je uitlogt en opnieuw inlogt!
De state wordt onbeperkt behouden dankzij de `localStorage`, maar dat betekent ook dat deze nooit wordt bijgewerkt totdat je uitlogt en opnieuw inlogt!
Een mogelijke strategie om dit op te lossen is om de accountgegevens elke keer dat het dashboard wordt geladen opnieuw te laden, om verouderde gegevens te voorkomen.
Een mogelijke strategie om dat op te lossen is om de accountgegevens opnieuw te laden telkens wanneer het dashboard wordt geladen, om verouderde gegevens te voorkomen.
### Taak
@ -258,7 +258,7 @@ async function refresh() {
}
```
Deze functie werkt de accountgegevens bij en zorgt er vervolgens voor dat de HTML van de dashboardpagina wordt bijgewerkt. Dit is wat we moeten aanroepen wanneer de dashboardroute wordt geladen. Werk de routedefinitie bij met:
Deze functie werkt de accountgegevens bij en zorgt vervolgens voor het bijwerken van de HTML van de dashboardpagina. Dit is wat we moeten aanroepen wanneer de dashboardroute wordt geladen. Werk de routedefinitie bij met:
```js
const routes = {
@ -273,22 +273,22 @@ Probeer nu het dashboard te verversen, het zou de bijgewerkte accountgegevens mo
## 🚀 Uitdaging
Nu we de accountgegevens elke keer dat het dashboard wordt geladen opnieuw ophalen, denk je dat we nog steeds *alle accountgegevens* moeten behouden?
Nu we de accountgegevens opnieuw laden telkens wanneer het dashboard wordt geladen, denk je dat we nog steeds *alle accountgegevens* moeten behouden?
Probeer samen te werken om te wijzigen wat wordt opgeslagen en geladen vanuit `localStorage`, zodat alleen wordt opgeslagen wat absoluut noodzakelijk is voor de app om te werken.
## Quiz na de les
Probeer samen te werken om te veranderen wat wordt opgeslagen en geladen vanuit `localStorage` zodat alleen wordt opgenomen wat absoluut nodig is voor de app om te werken.
## Post-Lecture Quiz
[Quiz na de les](https://ff-quizzes.netlify.app/web/quiz/48)
Hier is een voorbeeldresultaat na het voltooien van de opdracht:
[Implementeer de dialoog "Transactie toevoegen"](assignment.md)
Hier is een voorbeeld van het resultaat na het voltooien van de opdracht:


---
**Disclaimer**:
Dit document is vertaald met behulp van de AI-vertalingsservice [Co-op Translator](https://github.com/Azure/co-op-translator). Hoewel we streven naar nauwkeurigheid, dient u zich ervan bewust te zijn dat geautomatiseerde vertalingen fouten of onnauwkeurigheden kunnen bevatten. Het originele document in de oorspronkelijke taal moet worden beschouwd als de gezaghebbende bron. Voor kritieke informatie wordt professionele menselijke vertaling aanbevolen. Wij zijn niet aansprakelijk voor misverstanden of verkeerde interpretaties die voortvloeien uit het gebruik van deze vertaling.
Dit document is vertaald met behulp van de AI-vertalingsservice [Co-op Translator](https://github.com/Azure/co-op-translator). Hoewel we streven naar nauwkeurigheid, dient u zich ervan bewust te zijn dat geautomatiseerde vertalingen fouten of onnauwkeurigheden kunnen bevatten. Het originele document in de oorspronkelijke taal moet worden beschouwd als de gezaghebbende bron. Voor cruciale informatie wordt professionele menselijke vertaling aanbevolen. Wij zijn niet aansprakelijk voor misverstanden of verkeerde interpretaties die voortvloeien uit het gebruik van deze vertaling.
Etter hvert som en webapplikasjon vokser, blir det en utfordring å holde oversikt over alle dataflyter. Hvilken kode henter data, hvilken side bruker den, hvor og når må den oppdateres...det er lett å ende opp med rotete kode som er vanskelig å vedlikeholde. Dette gjelder spesielt når du må dele data mellom forskjellige sider i appen din, for eksempel brukerdata. Konseptet *tilstandshåndtering* har alltid eksistert i alle typer programmer, men ettersom webapplikasjoner blir stadig mer komplekse, har det blitt et nøkkelpunkt å tenke på under utviklingen.
Etter hvert som en webapplikasjon vokser, blir det en utfordring å holde oversikt over alle dataflyter. Hvilken kode henter dataene, hvilken side bruker dem, hvor og når må de oppdateres... Det er lett å ende opp med rotete kode som er vanskelig å vedlikeholde. Dette gjelder spesielt når du må dele data mellom forskjellige sider i appen din, for eksempel brukerdata. Konseptet *tilstandshåndtering* har alltid eksistert i alle typer programmer, men ettersom webapplikasjoner blir stadig mer komplekse, har det blitt et nøkkelpunkt å tenke på under utviklingen.
I denne siste delen skal vi se på appen vi har bygget for å revurdere hvordan tilstanden håndteres, slik at vi kan støtte nettleseroppdatering når som helst og bevare data på tvers av brukersesjoner.
I denne siste delen skal vi se på appen vi har bygget og revurdere hvordan tilstanden håndteres, slik at vi kan støtte nettleseroppdatering når som helst og bevare data på tvers av brukersesjoner.
### Forutsetninger
@ -40,7 +40,7 @@ Det er tre problemer med den nåværende koden:
- Tilstanden blir ikke bevart, da en nettleseroppdatering tar deg tilbake til innloggingssiden.
- Det finnes flere funksjoner som endrer tilstanden. Etter hvert som appen vokser, kan det bli vanskelig å holde oversikt over endringene, og det er lett å glemme å oppdatere én.
- Tilstanden blir ikke ryddet opp, så når du klikker på *Logg ut*, er kontodataene fortsatt der selv om du er på innloggingssiden.
- Tilstanden blir ikke ryddet opp, så når du klikker på *Logg ut*, er kontodataene fortsatt der, selv om du er på innloggingssiden.
Vi kunne oppdatert koden vår for å takle disse problemene én etter én, men det ville skapt mer kode duplisering og gjort appen mer kompleks og vanskelig å vedlikeholde. Eller vi kunne tatt oss noen minutter til å revurdere strategien vår.
@ -51,11 +51,11 @@ Vi kunne oppdatert koden vår for å takle disse problemene én etter én, men d
- Hvordan holde dataflytene i en app forståelige?
- Hvordan holde tilstandsdata alltid synkronisert med brukergrensesnittet (og vice versa)?
Når du har tatt hånd om disse, kan eventuelle andre problemer du har enten allerede være løst eller blitt enklere å fikse. Det finnes mange mulige tilnærminger for å løse disse problemene, men vi skal gå for en vanlig løsning som består av **å sentralisere dataene og måtene å endre dem på**. Dataflytene vil se slik ut:
Når du har tatt hånd om disse, kan eventuelle andre problemer du har enten være løst allerede eller blitt enklere å fikse. Det finnes mange mulige tilnærminger for å løse disse problemene, men vi skal gå for en vanlig løsning som består av **å sentralisere dataene og måtene å endre dem på**. Dataflytene vil se slik ut:

> Vi dekker ikke her delen der data automatisk utløser oppdatering av visningen, da det er knyttet til mer avanserte konsepter innen [Reaktiv programmering](https://en.wikipedia.org/wiki/Reactive_programming). Det er et godt oppfølgingsemne hvis du ønsker å dykke dypere.
> Vi dekker ikke her delen der data automatisk utløser oppdatering av visningen, da det er knyttet til mer avanserte konsepter innen [reaktiv programmering](https://en.wikipedia.org/wiki/Reactive_programming). Det er et godt oppfølgingsemne hvis du ønsker å dykke dypere.
✅ Det finnes mange biblioteker der ute med forskjellige tilnærminger til tilstandshåndtering, [Redux](https://redux.js.org) er et populært alternativ. Ta en titt på konseptene og mønstrene som brukes, da det ofte er en god måte å lære om potensielle problemer du kan møte i store webapplikasjoner og hvordan de kan løses.
@ -91,7 +91,7 @@ Denne refaktoreringen i seg selv har ikke gitt store forbedringer, men ideen var
Nå som vi har satt opp `state`-objektet for å lagre dataene våre, er neste steg å sentralisere oppdateringene. Målet er å gjøre det enklere å holde oversikt over eventuelle endringer og når de skjer.
For å unngå at endringer gjøres direkte på `state`-objektet, er det også en god praksis å betrakte det som [*uendelig*](https://en.wikipedia.org/wiki/Immutable_object), noe som betyr at det ikke kan endres i det hele tatt. Det betyr også at du må opprette et nytt tilstandsobjekt hvis du vil endre noe i det. Ved å gjøre dette, bygger du en beskyttelse mot potensielt uønskede [sideeffekter](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), og åpner opp muligheter for nye funksjoner i appen din, som å implementere angre/gjenta, samtidig som det blir enklere å feilsøke. For eksempel kan du logge hver endring som gjøres i tilstanden og holde en historikk over endringene for å forstå kilden til en feil.
For å unngå at endringer gjøres direkte på `state`-objektet, er det også en god praksis å betrakte det som [*uendelig*](https://en.wikipedia.org/wiki/Immutable_object), noe som betyr at det ikke kan endres i det hele tatt. Det betyr også at du må opprette et nytt tilstandsobjekt hvis du vil endre noe i det. Ved å gjøre dette bygger du en beskyttelse mot potensielt uønskede [sideeffekter](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), og åpner opp muligheter for nye funksjoner i appen din, som å implementere angre/gjenta, samtidig som det blir enklere å feilsøke. For eksempel kan du logge hver endring som gjøres i tilstanden og holde en historikk over endringene for å forstå kilden til en feil.
I JavaScript kan du bruke [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) for å opprette en uendelig versjon av et objekt. Hvis du prøver å gjøre endringer på et uendelig objekt, vil det oppstå en unntak.
@ -99,7 +99,7 @@ I JavaScript kan du bruke [`Object.freeze()`](https://developer.mozilla.org/docs
### Oppgave
La oss opprette en ny `updateState()`-funksjon:
La oss lage en ny `updateState()`-funksjon:
```js
function updateState(property, newData) {
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
I denne funksjonen oppretter vi et nytt tilstandsobjekt og kopierer data fra den forrige tilstanden ved hjelp av [*spread (`...`) operatoren*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Deretter overstyrer vi en bestemt egenskap i tilstandsobjektet med de nye dataene ved hjelp av [brakettnotasjon](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` for tildeling. Til slutt låser vi objektet for å forhindre endringer ved hjelp av `Object.freeze()`. Vi har bare `account`-egenskapen lagret i tilstanden for nå, men med denne tilnærmingen kan du legge til så mange egenskaper du trenger i tilstanden.
I denne funksjonen oppretter vi et nytt tilstandsobjekt og kopierer data fra den forrige tilstanden ved hjelp av [*spread (`...`) operatoren*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Deretter overstyrer vi en bestemt egenskap i tilstandsobjektet med de nye dataene ved hjelp av [brakettnotasjon](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` for tildeling. Til slutt låser vi objektet for å forhindre endringer ved hjelp av `Object.freeze()`. Vi har foreløpig bare `account`-egenskapen lagret i tilstanden, men med denne tilnærmingen kan du legge til så mange egenskaper du trenger i tilstanden.
Vi oppdaterer også `state`-initialiseringen for å sikre at den opprinnelige tilstanden også er låst:
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
Etter dette, oppdater `register`-funksjonen ved å erstatte `state.account = result;`-tildelingen med:
Etter det, oppdater `register`-funksjonen ved å erstatte `state.account = result;`-tildelingen med:
```js
updateState('account', result);
@ -158,20 +158,20 @@ Når du ønsker å bevare data i nettleseren, er det noen viktige spørsmål du
- *Er dataene sensitive?* Du bør unngå å lagre sensitive data på klienten, som brukerpassord.
- *Hvor lenge trenger du å beholde disse dataene?* Planlegger du å aksessere disse dataene kun for den nåværende sesjonen, eller ønsker du at de skal lagres for alltid?
Det finnes flere måter å lagre informasjon i en webapp, avhengig av hva du ønsker å oppnå. For eksempel kan du bruke URL-er til å lagre et søkespørsmål og gjøre det delbart mellom brukere. Du kan også bruke [HTTP-cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) hvis dataene må deles med serveren, som [autentiseringsinformasjon](https://en.wikipedia.org/wiki/Authentication).
Det finnes flere måter å lagre informasjon i en webapplikasjon, avhengig av hva du ønsker å oppnå. For eksempel kan du bruke URL-er til å lagre et søkespørsmål og gjøre det delbart mellom brukere. Du kan også bruke [HTTP-cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) hvis dataene må deles med serveren, som [autentiseringsinformasjon](https://en.wikipedia.org/wiki/Authentication).
En annen mulighet er å bruke en av de mange nettleser-API-ene for lagring av data. To av dem er spesielt interessante:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): en [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) som lar deg bevare data spesifikke for det nåværende nettstedet på tvers av forskjellige sesjoner. Dataene som lagres i det utløper aldri.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): denne fungerer på samme måte som `localStorage`, bortsett fra at dataene som lagres i det blir slettet når sesjonen avsluttes (når nettleseren lukkes).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): denne fungerer på samme måte som `localStorage`, bortsett fra at dataene som lagres i det slettes når sesjonen avsluttes (når nettleseren lukkes).
Merk at begge disse API-ene kun tillater lagring av [strenger](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Hvis du vil lagre komplekse objekter, må du serialisere dem til [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-formatet ved hjelp av [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Hvis du ønsker å lage en webapp som ikke fungerer med en server, er det også mulig å opprette en database på klienten ved hjelp av [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Dette er reservert for avanserte brukstilfeller eller hvis du trenger å lagre betydelige mengder data, da det er mer komplekst å bruke.
✅ Hvis du ønsker å lage en webapplikasjon som ikke fungerer med en server, er det også mulig å opprette en database på klienten ved hjelp av [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Dette er reservert for avanserte brukstilfeller eller hvis du trenger å lagre betydelige mengder data, da det er mer komplekst å bruke.
### Oppgave
Vi ønsker at brukerne skal forbli innlogget til de eksplisitt klikker på *Logg ut*-knappen, så vi bruker `localStorage` til å lagre kontodataene. Først definerer vi en nøkkel som vi skal bruke til å lagre dataene våre.
Vi ønsker at brukerne våre skal forbli innlogget til de eksplisitt klikker på *Logg ut*-knappen, så vi bruker `localStorage` til å lagre kontodataene. Først definerer vi en nøkkel som vi skal bruke til å lagre dataene våre.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Legg deretter til denne linjen på slutten av `updateState()`-funksjonen:
Med dette vil brukerkontodataene bli bevart og alltid være oppdatert ettersom vi tidligere sentraliserte alle tilstandsoppdateringene våre. Dette er hvor vi begynner å dra nytte av alle våre tidligere refaktorer 🙂.
Med dette vil brukerkontodataene bli bevart og alltid oppdatert ettersom vi tidligere sentraliserte alle tilstandsoppdateringene våre. Det er her vi begynner å dra nytte av alle våre tidligere refaktorer 🙂.
Siden dataene er lagret, må vi også ta oss av å gjenopprette dem når appen lastes. Siden vi begynner å få mer initialiseringskode, kan det være en god idé å opprette en ny `init`-funksjon, som også inkluderer vår tidligere kode nederst i `app.js`:
Siden dataene er lagret, må vi også ta oss av å gjenopprette dem når appen lastes. Siden vi begynner å ha mer initialiseringskode, kan det være en god idé å opprette en ny `init`-funksjon, som også inkluderer vår tidligere kode nederst i `app.js`:
```js
function init() {
@ -225,7 +225,7 @@ Prøv å oppdatere dashbordet i nettleseren nå. Hva skjer? Ser du den nye trans
Tilstanden blir bevart på ubestemt tid takket være `localStorage`, men det betyr også at den aldri blir oppdatert før du logger ut av appen og logger inn igjen!
En mulig strategi for å fikse dette er å laste inn kontodataene på nytt hver gang dashbordet lastes, for å unngå utdaterte data.
En mulig strategi for å fikse det er å laste inn kontodataene på nytt hver gang dashbordet lastes, for å unngå utdaterte data.
### Oppgave
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Denne metoden sjekker at vi er innlogget og laster deretter kontodataene på nytt fra serveren.
Denne metoden sjekker at vi er logget inn og laster deretter kontodataene på nytt fra serveren.
Opprett en annen funksjon kalt `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Denne oppdaterer kontodataene og tar deretter hånd om å oppdatere HTML-en på dashbordsiden. Det er det vi trenger å kalle når dashbordruten lastes. Oppdater rutedefinisjonen med:
Denne oppdaterer kontodataene og tar deretter hånd om å oppdatere HTML-en på dashbordet. Det er det vi trenger å kalle når dashbordruten lastes. Oppdater rutedefinisjonen med:
```js
const routes = {
@ -278,15 +278,15 @@ Nå som vi laster inn kontodataene på nytt hver gang dashbordet lastes, tror du
Prøv å jobbe sammen for å endre hva som lagres og lastes fra `localStorage` til kun å inkludere det som er absolutt nødvendig for at appen skal fungere.
## Quiz etter forelesning
[Quiz etter forelesning](https://ff-quizzes.netlify.app/web/quiz/48)
## Oppgave
[Implementer "Legg til transaksjon"-dialog](assignment.md)
Her er et eksempelresultat etter å ha fullført oppgaven:
[Implementer dialogen "Legg til transaksjon"](assignment.md)
Her er et eksempel på resultatet etter å ha fullført oppgaven:


Wraz z rozwojem aplikacji internetowej, śledzenie przepływów danych staje się coraz trudniejsze. Który kod pobiera dane, która strona je wykorzystuje, gdzie i kiedy należy je zaktualizować... łatwo skończyć z chaotycznym kodem, który trudno utrzymać. Jest to szczególnie problematyczne, gdy dane muszą być współdzielone między różnymi stronami aplikacji, na przykład dane użytkownika. Koncepcja *zarządzania stanem* zawsze istniała w różnego rodzaju programach, ale wraz ze wzrostem złożoności aplikacji internetowych stała się kluczowym punktem do rozważenia podczas ich tworzenia.
Wraz z rozwojem aplikacji internetowej śledzenie przepływów danych staje się coraz trudniejsze. Który kod pobiera dane, która strona je wykorzystuje, gdzie i kiedy należy je zaktualizować... łatwo skończyć z chaotycznym kodem, który trudno utrzymać. Jest to szczególnie problematyczne, gdy trzeba udostępniać dane między różnymi stronami aplikacji, na przykład dane użytkownika. Koncepcja *zarządzania stanem* zawsze istniała w różnego rodzaju programach, ale wraz ze wzrostem złożoności aplikacji internetowych stała się kluczowym punktem do przemyślenia podczas ich tworzenia.
W tej ostatniej części przyjrzymy się aplikacji, którą zbudowaliśmy, aby przemyśleć sposób zarządzania stanem, umożliwiając obsługę odświeżania przeglądarki w dowolnym momencie oraz utrzymywanie danych między sesjami użytkownika.
### Wymagania wstępne
Musisz ukończyć część dotyczącą [pobierania danych](../3-data/README.md) aplikacji internetowej z tej lekcji. Musisz także zainstalować [Node.js](https://nodejs.org) i [uruchomić lokalnie serwer API](../api/README.md), aby zarządzać danymi konta.
Musisz ukończyć część dotyczącą [pobierania danych](../3-data/README.md) w aplikacji internetowej. Musisz także zainstalować [Node.js](https://nodejs.org) i [uruchomić lokalnie serwer API](../api/README.md), aby zarządzać danymi konta.
Możesz sprawdzić, czy serwer działa poprawnie, wykonując to polecenie w terminalu:
@ -34,40 +34,40 @@ curl http://localhost:5000/api
## Przemyślenie zarządzania stanem
W [poprzedniej lekcji](../3-data/README.md) wprowadziliśmy podstawową koncepcję stanu w naszej aplikacji za pomocą globalnej zmiennej `account`, która zawiera dane bankowe aktualnie zalogowanego użytkownika. Jednak nasza obecna implementacja ma pewne wady. Spróbuj odświeżyć stronę, będąc na pulpicie. Co się dzieje?
W [poprzedniej lekcji](../3-data/README.md) wprowadziliśmy podstawową koncepcję stanu w naszej aplikacji za pomocą globalnej zmiennej `account`, która zawiera dane bankowe aktualnie zalogowanego użytkownika. Jednak nasza obecna implementacja ma pewne wady. Spróbuj odświeżyć stronę, gdy jesteś na pulpicie nawigacyjnym. Co się dzieje?
Obecny kod ma 3 problemy:
Obecny kod ma trzy problemy:
- Stan nie jest utrwalany, ponieważ odświeżenie przeglądarki przenosi Cię z powrotem na stronę logowania.
- Istnieje wiele funkcji, które modyfikują stan. W miarę rozwoju aplikacji może to utrudniać śledzenie zmian i łatwo zapomnieć o aktualizacji jednej z nich.
- Stan nie jest czyszczony, więc po kliknięciu *Wyloguj się* dane konta nadal tam są, mimo że jesteś na stronie logowania.
- Istnieje wiele funkcji, które modyfikują stan. Wraz z rozwojem aplikacji może to utrudnić śledzenie zmian i łatwo zapomnieć o ich aktualizacji.
- Stan nie jest czyszczony, więc po kliknięciu *Wyloguj się* dane konta nadal są dostępne, mimo że jesteś na stronie logowania.
Moglibyśmy zaktualizować nasz kod, aby rozwiązać te problemy jeden po drugim, ale spowodowałoby to powielenie kodu i uczyniło aplikację bardziej złożoną i trudniejszą w utrzymaniu. Albo moglibyśmy zatrzymać się na chwilę i przemyśleć naszą strategię.
Moglibyśmy zaktualizować nasz kod, aby rozwiązać te problemy pojedynczo, ale spowodowałoby to powielenie kodu i uczyniło aplikację bardziej skomplikowaną i trudniejszą w utrzymaniu. Albo moglibyśmy zatrzymać się na chwilę i przemyśleć naszą strategię.
> Jakie problemy tak naprawdę próbujemy tutaj rozwiązać?
[Zarządzanie stanem](https://en.wikipedia.org/wiki/State_management) polega na znalezieniu dobrego podejścia do rozwiązania tych dwóch konkretnych problemów:
- Jak utrzymać przepływy danych w aplikacji zrozumiałe?
- Jak utrzymać synchronizację danych stanu z interfejsem użytkownika (i vice versa)?
- Jak utrzymać przepływy danych w aplikacji w sposób zrozumiały?
- Jak utrzymać dane stanu zawsze zsynchronizowane z interfejsem użytkownika (i odwrotnie)?
Kiedy zajmiesz się tymi kwestiami, inne problemy, które możesz napotkać, mogą zostać już rozwiązane lub stać się łatwiejsze do rozwiązania. Istnieje wiele możliwych podejść do rozwiązania tych problemów, ale my wybierzemy popularne rozwiązanie polegające na **centralizacji danych i sposobów ich zmiany**. Przepływy danych będą wyglądały tak:
Gdy zajmiesz się tymi kwestiami, inne problemy, które możesz napotkać, mogą zostać już rozwiązane lub stać się łatwiejsze do naprawienia. Istnieje wiele możliwych podejść do rozwiązania tych problemów, ale my zastosujemy popularne rozwiązanie polegające na **centralizacji danych i sposobów ich zmiany**. Przepływy danych wyglądałyby następująco:

> Nie omówimy tutaj części, w której dane automatycznie wyzwalają aktualizację widoku, ponieważ jest to związane z bardziej zaawansowanymi koncepcjami [programowania reaktywnego](https://en.wikipedia.org/wiki/Reactive_programming). To dobry temat do zgłębienia, jeśli chcesz się zagłębić.
> Nie będziemy tutaj omawiać części, w której dane automatycznie wyzwalają aktualizację widoku, ponieważ jest to związane z bardziej zaawansowanymi koncepcjami [programowania reaktywnego](https://en.wikipedia.org/wiki/Reactive_programming). To dobry temat do zgłębienia, jeśli chcesz się zagłębić.
✅ Istnieje wiele bibliotek z różnymi podejściami do zarządzania stanem, a [Redux](https://redux.js.org) jest popularną opcją. Zapoznaj się z koncepcjami i wzorcami, które są w nich używane, ponieważ często jest to dobry sposób na zrozumienie potencjalnych problemów, z którymi możesz się zmierzyć w dużych aplikacjach internetowych, oraz sposobów ich rozwiązania.
✅ Istnieje wiele bibliotek oferujących różne podejścia do zarządzania stanem, z których popularną opcją jest [Redux](https://redux.js.org). Zapoznaj się z koncepcjami i wzorcami, które są w niej stosowane, ponieważ często jest to dobry sposób na zrozumienie potencjalnych problemów, z jakimi możesz się zmierzyć w dużych aplikacjach internetowych, oraz sposobów ich rozwiązania.
### Zadanie
Zaczniemy od niewielkiego refaktoringu. Zastąp deklarację `account`:
Zaczniemy od niewielkiego refaktoringu. Zamień deklarację `account`:
```js
let account = null;
```
Tym:
Na:
```js
let state = {
@ -75,9 +75,9 @@ let state = {
};
```
Pomysł polega na *centralizacji* wszystkich danych aplikacji w jednym obiekcie stanu. Na razie mamy tylko `account` w stanie, więc niewiele się zmienia, ale tworzy to ścieżkę do przyszłych zmian.
Pomysł polega na *centralizacji* wszystkich danych aplikacji w jednym obiekcie stanu. Na razie mamy tylko `account` w stanie, więc niewiele się zmienia, ale tworzy to podstawę do dalszego rozwoju.
Musimy również zaktualizować funkcje, które go używają. W funkcjach `register()` i `login()` zastąp `account = ...` przez`state.account = ...`;
Musimy także zaktualizować funkcje, które go używają. W funkcjach `register()` i `login()` zamień `account = ...` na`state.account = ...`;
Na początku funkcji `updateDashboard()` dodaj tę linię:
@ -85,17 +85,17 @@ Na początku funkcji `updateDashboard()` dodaj tę linię:
const account = state.account;
```
Sam ten refaktoring nie przyniósł dużych ulepszeń, ale pomysł polegał na stworzeniu fundamentu pod kolejne zmiany.
Sam ten refaktoring nie przyniósł dużych ulepszeń, ale jego celem było stworzenie podstawy pod kolejne zmiany.
## Śledzenie zmian danych
Teraz, gdy wprowadziliśmy obiekt `state` do przechowywania naszych danych, następnym krokiem jest centralizacja aktualizacji. Celem jest ułatwienie śledzenia wszelkich zmian i momentów, w których się one pojawiają.
Teraz, gdy wprowadziliśmy obiekt `state` do przechowywania danych, kolejnym krokiem jest centralizacja aktualizacji. Celem jest ułatwienie śledzenia wszelkich zmian i momentów, w których one występują.
Aby uniknąć wprowadzania zmian w obiekcie `state`, dobrą praktyką jest również uznanie go za [*niezmienny*](https://en.wikipedia.org/wiki/Immutable_object), co oznacza, że nie można go w ogóle modyfikować. Oznacza to również, że musisz utworzyć nowy obiekt stanu, jeśli chcesz coś w nim zmienić. Dzięki temu budujesz ochronę przed potencjalnie niepożądanymi [efektami ubocznymi](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) i otwierasz możliwości dla nowych funkcji w aplikacji, takich jak implementacja cofania/powtarzania, a także ułatwiasz debugowanie. Na przykład możesz rejestrować każdą zmianę w stanie i przechowywać historię zmian, aby zrozumieć źródło błędu.
Aby uniknąć wprowadzania zmian w obiekcie `state`, warto również traktować go jako [*niezmienny*](https://en.wikipedia.org/wiki/Immutable_object), co oznacza, że nie można go w ogóle modyfikować. Oznacza to również, że musisz utworzyć nowy obiekt stanu, jeśli chcesz coś w nim zmienić. Dzięki temu budujesz ochronę przed potencjalnie niepożądanymi [efektami ubocznymi](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) i otwierasz możliwości wprowadzenia nowych funkcji w aplikacji, takich jak implementacja cofania/powtarzania, a także ułatwiasz debugowanie. Na przykład możesz rejestrować każdą zmianę w stanie i przechowywać historię zmian, aby zrozumieć źródło błędu.
W JavaScript możesz użyć [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze), aby utworzyć niezmienną wersję obiektu. Jeśli spróbujesz wprowadzić zmiany w niezmiennym obiekcie, zostanie zgłoszony wyjątek.
✅ Czy wiesz, jaka jest różnica między *płytkim* a *głębokim* niezmiennym obiektem? Możesz o tym przeczytać [tutaj](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Czy wiesz, jaka jest różnica między obiektem *płytko* a *głęboko* niezmiennym? Możesz o tym przeczytać [tutaj](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Zadanie
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
W tej funkcji tworzymy nowy obiekt stanu i kopiujemy dane z poprzedniego stanu za pomocą [*operatora rozproszenia (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Następnie nadpisujemy konkretną właściwość obiektu stanu nowymi danymi, używając [notacji nawiasowej](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` do przypisania. Na koniec blokujemy obiekt, aby zapobiec modyfikacjom, używając `Object.freeze()`. Na razie w stanie przechowujemy tylko właściwość `account`, ale dzięki temu podejściu możesz dodać tyle właściwości, ile potrzebujesz.
W tej funkcji tworzymy nowy obiekt stanu i kopiujemy dane z poprzedniego stanu za pomocą [operatora *spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Następnie nadpisujemy konkretną właściwość obiektu stanu nowymi danymi, używając [notacji nawiasowej](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` do przypisania. Na koniec blokujemy obiekt, aby zapobiec modyfikacjom, używając `Object.freeze()`. Na razie przechowujemy w stanie tylko właściwość `account`, ale dzięki temu podejściu możesz dodać tyle właściwości, ile potrzebujesz.
Zaktualizujemy również inicjalizację `state`, aby upewnić się, że początkowy stan jest również zamrożony:
@ -120,19 +120,19 @@ let state = Object.freeze({
});
```
Następnie zaktualizuj funkcję `register`, zastępując przypisanie `state.account = result;` przez:
Następnie zaktualizuj funkcję `register`, zastępując przypisanie `state.account = result;`:
```js
updateState('account', result);
```
Zrób to samo z funkcją `login`, zastępując `state.account = data;` przez:
Zrób to samo z funkcją `login`, zastępując `state.account = data;`:
```js
updateState('account', data);
```
Teraz skorzystamy z okazji, aby naprawić problem z danymi konta, które nie są czyszczone, gdy użytkownik kliknie*Wyloguj się*.
Teraz skorzystamy z okazji, aby rozwiązać problem z danymi konta, które nie są czyszczone, gdy użytkownik klika*Wyloguj się*.
Utwórz nową funkcję `logout()`:
@ -143,27 +143,27 @@ function logout() {
}
```
W `updateDashboard()` zastąp przekierowanie `return navigate('/login');` przez `return logout();`
W `updateDashboard()` zamień przekierowanie `return navigate('/login');` na `return logout()`;
Spróbuj zarejestrować nowe konto, wylogować się i zalogować ponownie, aby sprawdzić, czy wszystko nadal działa poprawnie.
Spróbuj zarejestrować nowe konto, wylogować się i zalogować ponownie, aby sprawdzić, czy wszystko działa poprawnie.
> Wskazówka: możesz przejrzeć wszystkie zmiany stanu, dodając `console.log(state)` na końcu `updateState()` i otwierając konsolę w narzędziach deweloperskich przeglądarki.
## Utrwalanie stanu
Większość aplikacji internetowych musi utrwalać dane, aby działać poprawnie. Wszystkie krytyczne dane są zwykle przechowywane w bazie danych i dostępne za pośrednictwem serwera API, tak jak dane konta użytkownika w naszym przypadku. Ale czasami warto również utrwalać pewne dane w aplikacji klienckiej działającej w przeglądarce, aby poprawić doświadczenie użytkownika lub zwiększyć wydajność ładowania.
Większość aplikacji internetowych musi utrwalać dane, aby działać poprawnie. Wszystkie krytyczne dane są zwykle przechowywane w bazie danych i dostępne za pośrednictwem serwera API, jak w przypadku danych konta użytkownika. Ale czasami warto również utrwalać pewne dane w aplikacji klienckiej działającej w przeglądarce, aby poprawić doświadczenie użytkownika lub zwiększyć wydajność ładowania.
Kiedy chcesz utrwalać dane w przeglądarce, warto zadać sobie kilka ważnych pytań:
- *Czy dane są wrażliwe?*Powinieneś unikać przechowywania wrażliwych danych po stronie klienta, takich jak hasła użytkownika.
- *Czy dane są wrażliwe?*Należy unikać przechowywania wrażliwych danych po stronie klienta, takich jak hasła użytkowników.
- *Jak długo potrzebujesz przechowywać te dane?* Czy planujesz korzystać z tych danych tylko podczas bieżącej sesji, czy chcesz, aby były przechowywane na stałe?
Istnieje wiele sposobów przechowywania informacji w aplikacji internetowej, w zależności od tego, co chcesz osiągnąć. Na przykład możesz użyć adresów URL do przechowywania zapytania wyszukiwania i udostępniania go między użytkownikami. Możesz także użyć [ciasteczek HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies), jeśli dane muszą być współdzielone z serwerem, na przykład informacje o [uwierzytelnianiu](https://en.wikipedia.org/wiki/Authentication).
Inną opcją jest użycie jednego z wielu API przeglądarki do przechowywania danych. Dwa z nich są szczególnie interesujące:
Inną opcją jest użycie jednej z wielu przeglądarkowych API do przechowywania danych. Dwie z nich są szczególnie interesujące:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Magazyn klucz/wartość](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), który pozwala na utrwalanie danych specyficznych dla bieżącej strony internetowej między różnymi sesjami. Dane zapisane w nim nigdy nie wygasają.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): działa tak samo jak `localStorage`, z tą różnicą, że dane w nim przechowywane są usuwane po zakończeniu sesji (po zamknięciu przeglądarki).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): działa tak samo jak `localStorage`, z tą różnicą, że dane przechowywane w nim są usuwane po zakończeniu sesji (po zamknięciu przeglądarki).
Należy pamiętać, że oba te API pozwalają przechowywać tylko [ciągi znaków](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Jeśli chcesz przechowywać złożone obiekty, musisz je zserializować do formatu [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) za pomocą [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
@ -171,7 +171,7 @@ Należy pamiętać, że oba te API pozwalają przechowywać tylko [ciągi znakó
### Zadanie
Chcemy, aby nasi użytkownicy pozostawali zalogowani, dopóki nie klikną wyraźnie przycisku *Wyloguj się*, więc użyjemy `localStorage` do przechowywania danych konta. Najpierw zdefiniujmy klucz, którego użyjemy do przechowywania naszych danych.
Chcemy, aby nasi użytkownicy pozostawali zalogowani, dopóki nie klikną przycisku *Wyloguj się*, więc użyjemy `localStorage` do przechowywania danych konta. Najpierw zdefiniujmy klucz, którego użyjemy do przechowywania naszych danych.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Następnie dodaj tę linię na końcu funkcji `updateState()`:
Dzięki temu dane konta użytkownika będą utrwalane i zawsze aktualne, ponieważ wcześniej scentralizowaliśmy wszystkie aktualizacje stanu. To tutaj zaczynamy czerpać korzyści z wcześniejszych refaktorów 🙂.
Dzięki temu dane konta użytkownika będą utrwalane i zawsze aktualne, ponieważ wcześniej scentralizowaliśmy wszystkie aktualizacje stanu. To właśnie tutaj zaczynamy czerpać korzyści z wcześniejszych refaktoryzacji 🙂.
Ponieważ dane są zapisywane, musimy również zadbać o ich przywracanie podczas ładowania aplikacji. Ponieważ zaczniemy mieć więcej kodu inicjalizacyjnego, warto utworzyć nową funkcję `init`, która również zawiera nasz poprzedni kod na końcu `app.js`:
Ponieważ dane są zapisywane, musimy również zadbać o ich przywracanie podczas ładowania aplikacji. Ponieważ zaczniemy mieć więcej kodu inicjalizacyjnego, warto utworzyć nową funkcję `init`, która obejmuje również nasz wcześniejszy kod na końcu `app.js`:
```js
function init() {
@ -202,11 +202,11 @@ function init() {
init();
```
Tutaj pobieramy zapisane dane, a jeśli jakieś istnieją, aktualizujemy stan odpowiednio. Ważne jest, aby zrobić to *przed* aktualizacją trasy, ponieważ może istnieć kod polegający na stanie podczas aktualizacji strony.
Tutaj pobieramy zapisane dane, a jeśli jakieś istnieją, aktualizujemy stan odpowiednio. Ważne jest, aby zrobić to *przed* aktualizacją trasy, ponieważ może istnieć kod, który polega na stanie podczas aktualizacji strony.
Możemy również ustawić stronę *Dashboard* jako domyślną stronę naszej aplikacji, ponieważ teraz utrwalamy dane konta. Jeśli nie znaleziono danych, pulpit nawigacyjny i tak zajmuje się przekierowaniem na stronę *Login*. W `updateRoute()` zastąp domyślne `return navigate('/login');` przez`return navigate('/dashboard');`.
Możemy również ustawić stronę *Dashboard* jako domyślną stronę naszej aplikacji, ponieważ teraz utrwalamy dane konta. Jeśli nie znaleziono danych, pulpit nawigacyjny i tak zajmuje się przekierowaniem na stronę *Login*. W `updateRoute()` zamień domyślne `return navigate('/login');` na`return navigate('/dashboard');`.
Teraz zaloguj się do aplikacji i spróbuj odświeżyć stronę. Powinieneś pozostać na pulpicie. Dzięki tej aktualizacji rozwiązaliśmy wszystkie nasze początkowe problemy...
Zaloguj się teraz do aplikacji i spróbuj odświeżyć stronę. Powinieneś pozostać na pulpicie nawigacyjnym. Dzięki tej aktualizacji rozwiązaliśmy wszystkie nasze początkowe problemy...
Spróbuj teraz odświeżyć stronę pulpitu w przeglądarce. Co się dzieje? Czy widzisz nową transakcję?
Spróbuj teraz odświeżyć stronę pulpitu nawigacyjnego w przeglądarce. Co się dzieje? Czy widzisz nową transakcję?
Stan jest utrwalany na stałe dzięki `localStorage`, ale oznacza to również, że nigdy nie jest aktualizowany, dopóki nie wylogujesz się z aplikacji i nie zalogujesz ponownie!
Stan jest utrwalany na stałe dzięki `localStorage`, ale to oznacza, że nigdy nie jest aktualizowany, dopóki nie wylogujesz się z aplikacji i nie zalogujesz ponownie!
Jedną z możliwych strategii naprawienia tego problemu jest ponowne ładowanie danych konta za każdym razem, gdy ładowany jest pulpit nawigacyjny, aby uniknąć przestarzałych danych.
Jedną z możliwych strategii rozwiązania tego problemu jest ponowne ładowanie danych konta za każdym razem, gdy ładowany jest pulpit nawigacyjny, aby uniknąć przestarzałych danych.
### Zadanie
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Ta metoda sprawdza, czy jesteśmy obecnie zalogowani, a następnie ponownie ładuje dane konta z serwera.
Ta metoda sprawdza, czy jesteśmy aktualnie zalogowani, a następnie ponownie ładuje dane konta z serwera.
Utwórz kolejną funkcję o nazwie `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Ta funkcja aktualizuje dane konta, a następnie zajmuje się aktualizacją HTML strony pulpitu nawigacyjnego. To właśnie ją musimy wywołać, gdy ładowana jest trasa pulpitu. Zaktualizuj definicję trasy za pomocą:
Ta funkcja aktualizuje dane konta, a następnie zajmuje się aktualizacją HTML strony pulpitu nawigacyjnego. To właśnie ją musimy wywołać, gdy ładowana jest trasa pulpitu nawigacyjnego. Zaktualizuj definicję trasy:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
Spróbuj teraz odświeżyć pulpit, powinien wyświetlać zaktualizowane dane konta.
Spróbuj teraz odświeżyć pulpit nawigacyjny, powinien wyświetlać zaktualizowane dane konta.
---
## 🚀 Wyzwanie
Teraz, gdy za każdym razem, gdy ładowany jest pulpit, ponownie ładujemy dane konta, czy uważasz, że nadal musimy utrwalać *wszystkie dane konta*?
Teraz, gdy ponownie ładujemy dane konta za każdym razem, gdy ładowany jest pulpit nawigacyjny, czy uważasz, że nadal musimy utrwalać *wszystkie dane konta*?
Spróbujcie wspólnie zmienić to, co jest zapisywane i ładowane z `localStorage`, aby obejmowało tylko to, co jest absolutnie wymagane do działania aplikacji.
## Quiz po wykładzie
[Quiz po wykładzie](https://ff-quizzes.netlify.app/web/quiz/48)
Ten dokument został przetłumaczony za pomocą usługi tłumaczenia AI [Co-op Translator](https://github.com/Azure/co-op-translator). Chociaż staramy się zapewnić dokładność, prosimy mieć na uwadze, że automatyczne tłumaczenia mogą zawierać błędy lub nieścisłości. Oryginalny dokument w jego rodzimym języku powinien być uznawany za wiarygodne źródło. W przypadku informacji krytycznych zaleca się skorzystanie z profesjonalnego tłumaczenia wykonanego przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.
Ten dokument został przetłumaczony za pomocą usługi tłumaczeniowej AI [Co-op Translator](https://github.com/Azure/co-op-translator). Chociaż dokładamy wszelkich starań, aby tłumaczenie było precyzyjne, prosimy pamiętać, że automatyczne tłumaczenia mogą zawierać błędy lub nieścisłości. Oryginalny dokument w jego rodzimym języku powinien być uznawany za wiarygodne źródło. W przypadku informacji krytycznych zaleca się skorzystanie z profesjonalnego tłumaczenia wykonanego przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z korzystania z tego tłumaczenia.
# Construir uma App Bancária Parte 4: Conceitos de Gestão de Estado
# Criar uma App Bancária Parte 4: Conceitos de Gestão de Estado
## Questionário Pré-Aula
@ -15,9 +15,9 @@ CO_OP_TRANSLATOR_METADATA:
### Introdução
À medida que uma aplicação web cresce, torna-se um desafio acompanhar todos os fluxos de dados. Qual código obtém os dados, qual página os consome, onde e quando precisam ser atualizados... é fácil acabar com um código confuso e difícil de manter. Isso é especialmente verdadeiro quando é necessário compartilhar dados entre diferentes páginas da aplicação, como os dados do utilizador. O conceito de *gestão de estado* sempre existiu em todos os tipos de programas, mas à medida que as aplicações web continuam a crescer em complexidade, tornou-se um ponto-chave a considerar durante o desenvolvimento.
À medida que uma aplicação web cresce, torna-se um desafio acompanhar todos os fluxos de dados. Que código obtém os dados, que página os consome, onde e quando precisam ser atualizados... é fácil acabar com um código desorganizado e difícil de manter. Isto é especialmente verdade quando é necessário partilhar dados entre diferentes páginas da aplicação, como os dados do utilizador. O conceito de *gestão de estado* sempre existiu em todos os tipos de programas, mas à medida que as aplicações web continuam a crescer em complexidade, tornou-se um ponto-chave a considerar durante o desenvolvimento.
Nesta última parte, vamos rever a aplicação que construímos para repensar como o estado é gerido, permitindo suporte para atualizações do navegador a qualquer momento e persistindo os dados entre sessões do utilizador.
Nesta última parte, vamos rever a aplicação que construímos para repensar como o estado é gerido, permitindo suporte para atualizações do navegador em qualquer momento e persistindo os dados entre sessões de utilizador.
### Pré-requisitos
@ -34,28 +34,28 @@ curl http://localhost:5000/api
## Repensar a gestão de estado
Na [lição anterior](../3-data/README.md), introduzimos um conceito básico de estado na nossa aplicação com a variável global `account`, que contém os dados bancários do utilizador atualmente autenticado. No entanto, a nossa implementação atual tem algumas falhas. Experimente atualizar a página enquanto está no painel. O que acontece?
Na [lição anterior](../3-data/README.md), introduzimos um conceito básico de estado na nossa aplicação com a variável global `account`, que contém os dados bancários do utilizador atualmente autenticado. No entanto, a nossa implementação atual tem algumas falhas. Experimente atualizar a página quando estiver no painel de controlo. O que acontece?
Há 3 problemas com o código atual:
- O estado não é persistido, pois uma atualização do navegador leva-o de volta à página de login.
- Existem várias funções que modificam o estado. À medida que a aplicação cresce, isso pode dificultar o acompanhamento das alterações e é fácil esquecer de atualizar algo.
- O estado não é limpo, então, quando clica em *Logout*, os dados da conta ainda estão lá, mesmo estando na página de login.
- Existem várias funções que modificam o estado. À medida que a aplicação cresce, pode tornar-se difícil acompanhar as alterações e é fácil esquecer de atualizar algo.
- O estado não é limpo, então quando clica em *Logout*, os dados da conta ainda estão lá, mesmo estando na página de login.
Poderíamos atualizar o nosso código para resolver esses problemas um por um, mas isso criaria mais duplicação de código e tornaria a aplicação mais complexa e difícil de manter. Ou poderíamos parar por alguns minutos e repensar a nossa estratégia.
Poderíamos atualizar o nosso código para resolver estes problemas um por um, mas isso criaria mais duplicação de código e tornaria a aplicação mais complexa e difícil de manter. Ou poderíamos parar por alguns minutos e repensar a nossa estratégia.
> Que problemas estamos realmente a tentar resolver aqui?
[A gestão de estado](https://en.wikipedia.org/wiki/State_management) trata de encontrar uma abordagem eficaz para resolver estes dois problemas específicos:
[Gestão de estado](https://en.wikipedia.org/wiki/State_management) trata de encontrar uma abordagem eficaz para resolver estes dois problemas específicos:
- Como manter os fluxos de dados numa aplicação compreensíveis?
- Como garantir que os dados do estado estejam sempre sincronizados com a interface do utilizador (e vice-versa)?
- Como manter os dados de estado sempre sincronizados com a interface do utilizador (e vice-versa)?
Depois de resolver esses problemas, quaisquer outros problemas que possa ter podem já estar resolvidos ou tornarem-se mais fáceis de resolver. Existem muitas abordagens possíveis para resolver esses problemas, mas vamos optar por uma solução comum que consiste em **centralizar os dados e as formas de os alterar**. Os fluxos de dados seriam assim:
Depois de resolver estes problemas, quaisquer outros que possam surgir podem já estar resolvidos ou tornar-se mais fáceis de resolver. Existem muitas abordagens possíveis para resolver estes problemas, mas vamos optar por uma solução comum que consiste em **centralizar os dados e as formas de os alterar**. Os fluxos de dados seriam assim:

> Não vamos abordar aqui a parte em que os dados automaticamente desencadeiam a atualização da vista, pois está ligada a conceitos mais avançados de [Programação Reativa](https://en.wikipedia.org/wiki/Reactive_programming). É um bom tema para explorar mais a fundo.
> Não vamos abordar aqui a parte em que os dados atualizam automaticamente a visualização, pois está ligada a conceitos mais avançados de [Programação Reativa](https://en.wikipedia.org/wiki/Reactive_programming). É um bom tema para explorar mais a fundo.
✅ Existem muitas bibliotecas com diferentes abordagens para gestão de estado, sendo o [Redux](https://redux.js.org) uma opção popular. Dê uma olhada nos conceitos e padrões utilizados, pois muitas vezes é uma boa forma de aprender sobre os potenciais problemas que pode enfrentar em grandes aplicações web e como podem ser resolvidos.
@ -91,9 +91,9 @@ Esta refatoração por si só não trouxe muitas melhorias, mas a ideia era prep
Agora que implementámos o objeto `state` para armazenar os dados, o próximo passo é centralizar as atualizações. O objetivo é facilitar o acompanhamento de quaisquer alterações e quando elas ocorrem.
Para evitar alterações feitas diretamente no objeto `state`, também é uma boa prática considerá-lo [*imutável*](https://en.wikipedia.org/wiki/Immutable_object), o que significa que não pode ser modificado de forma alguma. Isso também significa que é necessário criar um novo objeto de estado se quiser alterar algo nele. Ao fazer isso, constrói uma proteção contra potenciais [efeitos colaterais](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) indesejados e abre possibilidades para novas funcionalidades na aplicação, como implementar desfazer/refazer, além de facilitar a depuração. Por exemplo, poderia registar todas as alterações feitas no estado e manter um histórico das alterações para entender a origem de um erro.
Para evitar que sejam feitas alterações diretamente ao objeto `state`, também é uma boa prática considerá-lo [*imutável*](https://en.wikipedia.org/wiki/Immutable_object), o que significa que não pode ser modificado de forma alguma. Isso também implica que é necessário criar um novo objeto de estado se quiser alterar algo nele. Ao fazer isso, constrói-se uma proteção contra potenciais [efeitos colaterais](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) indesejados e abre-se possibilidades para novas funcionalidades na aplicação, como implementar desfazer/refazer, além de facilitar a depuração. Por exemplo, poderia registar todas as alterações feitas ao estado e manter um histórico das alterações para entender a origem de um erro.
Em JavaScript, pode usar [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) para criar uma versão imutável de um objeto. Se tentar fazer alterações num objeto imutável, será lançada uma exceção.
Em JavaScript, pode usar [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) para criar uma versão imutável de um objeto. Se tentar fazer alterações a um objeto imutável, será gerada uma exceção.
✅ Sabe a diferença entre um objeto imutável *superficial* e *profundo*? Pode ler sobre isso [aqui](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
Nesta função, estamos a criar um novo objeto de estado e a copiar os dados do estado anterior usando o [*operador de espalhamento (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Em seguida, substituímos uma propriedade específica do objeto de estado com os novos dados usando a [notação de colchetes](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para atribuição. Por fim, bloqueamos o objeto para evitar modificações usando `Object.freeze()`. Por enquanto, só temos a propriedade `account` armazenada no estado, mas com esta abordagem pode adicionar quantas propriedades forem necessárias.
Nesta função, estamos a criar um novo objeto de estado e a copiar os dados do estado anterior usando o [*operador de espalhamento (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Depois, substituímos uma propriedade específica do objeto de estado com os novos dados usando a [notação de colchetes](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para atribuição. Por fim, bloqueamos o objeto para evitar modificações usando `Object.freeze()`. Por enquanto, só temos a propriedade `account` armazenada no estado, mas com esta abordagem pode adicionar quantas propriedades forem necessárias.
Também vamos atualizar a inicialização do `state` para garantir que o estado inicial também seja congelado:
@ -143,7 +143,7 @@ function logout() {
}
```
Na função `updateDashboard()`, substitua o redirecionamento `return navigate('/login');` por `return logout();`;
Na função `updateDashboard()`, substitua a redireção `return navigate('/login');` por `return logout()`;
Experimente registar uma nova conta, fazer logout e login novamente para verificar se tudo ainda funciona corretamente.
@ -151,21 +151,21 @@ Experimente registar uma nova conta, fazer logout e login novamente para verific
## Persistir o estado
A maioria das aplicações web precisa de persistir dados para funcionar corretamente. Todos os dados críticos geralmente são armazenados numa base de dados e acessados através de uma API de servidor, como os dados da conta do utilizador no nosso caso. Mas, às vezes, também é interessante persistir alguns dados na aplicação cliente que está a ser executada no navegador, para uma melhor experiência do utilizador ou para melhorar o desempenho de carregamento.
A maioria das aplicações web precisa de persistir dados para funcionar corretamente. Todos os dados críticos geralmente são armazenados numa base de dados e acessados através de uma API de servidor, como os dados da conta de utilizador no nosso caso. Mas, por vezes, também é interessante persistir alguns dados na aplicação cliente que está a ser executada no navegador, para uma melhor experiência do utilizador ou para melhorar o desempenho de carregamento.
Quando quiser persistir dados no navegador, há algumas perguntas importantes que deve fazer:
- *Os dados são sensíveis?* Deve evitar armazenar quaisquer dados sensíveis no cliente, como senhas de utilizador.
- *Por quanto tempo precisa de manter esses dados?* Pretende acessar esses dados apenas durante a sessão atual ou quer que sejam armazenados para sempre?
- *Por quanto tempo precisa de manter estes dados?* Planeia aceder a estes dados apenas durante a sessão atual ou quer que sejam armazenados para sempre?
Existem várias formas de armazenar informações numa aplicação web, dependendo do que pretende alcançar. Por exemplo, pode usar os URLs para armazenar uma consulta de pesquisa e torná-la partilhável entre utilizadores. Também pode usar [cookies HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) se os dados precisarem de ser partilhados com o servidor, como informações de [autenticação](https://en.wikipedia.org/wiki/Authentication).
Outra opção é usar uma das muitas APIs do navegador para armazenar dados. Duas delas são particularmente interessantes:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): um [armazenamento de chave/valor](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) que permite persistir dados específicos do site atual entre diferentes sessões. Os dados guardados nele nunca expiram.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): um [armazenamento chave/valor](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) que permite persistir dados específicos do site atual entre diferentes sessões. Os dados guardados nele nunca expiram.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funciona da mesma forma que o `localStorage`, exceto que os dados armazenados nele são apagados quando a sessão termina (quando o navegador é fechado).
Note que ambas as APIs só permitem armazenar [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Se quiser armazenar objetos complexos, terá de os serializar para o formato [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) usando [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Note que ambas estas APIs só permitem armazenar [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Se quiser armazenar objetos complexos, terá de os serializar para o formato [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) usando [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Se quiser criar uma aplicação web que não funcione com um servidor, também é possível criar uma base de dados no cliente usando a API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Esta é reservada para casos de uso avançados ou se precisar de armazenar uma quantidade significativa de dados, pois é mais complexa de usar.
@ -183,9 +183,9 @@ Depois, adicione esta linha no final da função `updateState()`:
Com isso, os dados da conta do utilizador serão persistidos e sempre atualizados, já que centralizámos anteriormente todas as atualizações de estado. É aqui que começamos a beneficiar de todas as refatorações anteriores 🙂.
Com isto, os dados da conta do utilizador serão persistidos e sempre atualizados, já que centralizámos anteriormente todas as atualizações de estado. É aqui que começamos a beneficiar de todas as refatorações anteriores 🙂.
Como os dados são guardados, também temos de cuidar da sua restauração quando a aplicação é carregada. Como começaremos a ter mais código de inicialização, pode ser uma boa ideia criar uma nova função `init`, que também inclui o nosso código anterior no final de `app.js`:
Como os dados são guardados, também temos de cuidar de os restaurar quando a aplicação é carregada. Como começaremos a ter mais código de inicialização, pode ser uma boa ideia criar uma nova função `init`, que também inclui o nosso código anterior no final de `app.js`:
```js
function init() {
@ -204,15 +204,15 @@ init();
Aqui recuperamos os dados guardados e, se houver algum, atualizamos o estado de acordo. É importante fazer isso *antes* de atualizar a rota, pois pode haver código que depende do estado durante a atualização da página.
Também podemos tornar a página *Dashboard* a página padrão da nossa aplicação, já que agora estamos a persistir os dados da conta. Se nenhum dado for encontrado, o painel cuida de redirecionar para a página de *Login* de qualquer forma. Na função`updateRoute()`, substitua o fallback `return navigate('/login');` por `return navigate('/dashboard');`.
Também podemos tornar a página *Dashboard* a página padrão da nossa aplicação, já que agora estamos a persistir os dados da conta. Se não forem encontrados dados, o painel de controlo encarrega-se de redirecionar para a página de *Login* de qualquer forma. Em`updateRoute()`, substitua o fallback `return navigate('/login');` por `return navigate('/dashboard');`.
Agora faça login na aplicação e experimente atualizar a página. Deve permanecer no painel. Com essa atualização, resolvemos todos os problemas iniciais...
Agora faça login na aplicação e experimente atualizar a página. Deve permanecer no painel de controlo. Com esta atualização, resolvemos todos os problemas iniciais...
## Atualizar os dados
...Mas também podemos ter criado um novo problema. Ups!
Vá ao painel usando a conta `test`, depois execute este comando num terminal para criar uma nova transação:
Vá ao painel de controlo usando a conta `test`, depois execute este comando num terminal para criar uma nova transação:
Experimente atualizar o painel no navegador agora. O que acontece? Vê a nova transação?
Experimente atualizar a página do painel de controlo no navegador agora. O que acontece? Vê a nova transação?
O estado é persistido indefinidamente graças ao `localStorage`, mas isso também significa que nunca é atualizado até sair da aplicação e entrar novamente!
Uma possível estratégia para corrigir isso é recarregar os dados da conta sempre que o painel for carregado, para evitar dados desatualizados.
Uma possível estratégia para corrigir isso é recarregar os dados da conta sempre que o painel de controlo for carregado, para evitar dados desatualizados.
### Tarefa
@ -258,7 +258,7 @@ async function refresh() {
}
```
Esta função atualiza os dados da conta e, em seguida, cuida de atualizar o HTML da página do painel. É o que precisamos de chamar quando a rota do painel for carregada. Atualize a definição da rota com:
Esta função atualiza os dados da conta e, em seguida, cuida de atualizar o HTML da página do painel de controlo. É o que precisamos de chamar quando a rota do painel de controlo for carregada. Atualize a definição da rota com:
```js
const routes = {
@ -267,21 +267,21 @@ const routes = {
};
```
Experimente recarregar o painel agora, deve exibir os dados da conta atualizados.
Experimente recarregar o painel de controlo agora; deve exibir os dados da conta atualizados.
---
## 🚀 Desafio
Agora que recarregamos os dados da conta sempre que o painel é carregado, acha que ainda precisamos de persistir *todos os dados da conta*?
Agora que recarregamos os dados da conta sempre que o painel de controlo é carregado, acha que ainda precisamos de persistir *todos os dados da conta*?
Trabalhe em conjunto para alterar o que é guardado e carregado do `localStorage` para incluir apenas o que é absolutamente necessário para a aplicação funcionar.
Tente trabalhar em conjunto para alterar o que é guardado e carregado do `localStorage` para incluir apenas o que é absolutamente necessário para a aplicação funcionar.
[Implementar o diálogo "Adicionar transação"](assignment.md)
Aqui está um exemplo do resultado após concluir a tarefa:
@ -290,5 +290,5 @@ Aqui está um exemplo do resultado após concluir a tarefa:
---
**Aviso Legal**:
Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, é importante notar que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes da utilização desta tradução.
**Aviso**:
Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos pela precisão, esteja ciente de que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se uma tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes da utilização desta tradução.
Pe măsură ce o aplicație web crește, devine o provocare să urmărești toate fluxurile de date. Ce cod obține datele, ce pagină le consumă, unde și când trebuie actualizate... este ușor să ajungi la un cod dezordonat, dificil de întreținut. Acest lucru este cu atât mai adevărat atunci când trebuie să partajezi date între diferite pagini ale aplicației tale, de exemplu datele utilizatorului. Conceptul de *gestionare a stării* a existat întotdeauna în toate tipurile de programe, dar pe măsură ce aplicațiile web devin din ce în ce mai complexe, este acum un punct cheie de luat în considerare în timpul dezvoltării.
Pe măsură ce o aplicație web crește, devine o provocare să urmărești toate fluxurile de date. Ce cod obține datele, ce pagină le consumă, unde și când trebuie actualizate... este ușor să ajungi la un cod dezordonat, dificil de întreținut. Acest lucru este cu atât mai adevărat atunci când trebuie să partajezi date între diferite pagini ale aplicației tale, de exemplu datele utilizatorului. Conceptul de *gestionare a stării* a existat întotdeauna în toate tipurile de programe, dar pe măsură ce aplicațiile web devin din ce în ce mai complexe, acesta a devenit un punct cheie de luat în considerare în timpul dezvoltării.
În această ultimă parte, vom analiza aplicația pe care am construit-o pentru a regândi modul în care este gestionată starea, permițând suportul pentru reîmprospătarea browserului în orice moment și păstrarea datelor între sesiunile utilizatorului.
### Prerechizite
Trebuie să fi finalizat partea de [preluare a datelor](../3-data/README.md) a aplicației web pentru această lecție. De asemenea, trebuie să instalezi [Node.js](https://nodejs.org) și să [rulezi serverul API](../api/README.md) local pentru a putea gestiona datele contului.
Trebuie să fi finalizat partea de [preluare a datelor](../3-data/README.md) a aplicației web pentru această lecție. De asemenea, trebuie să instalați [Node.js](https://nodejs.org) și să [rulați serverul API](../api/README.md) local pentru a putea gestiona datele contului.
Poți testa dacă serverul funcționează corect executând această comandă într-un terminal:
Puteți testa dacă serverul funcționează corect executând această comandă într-un terminal:
```sh
curl http://localhost:5000/api
@ -34,34 +34,34 @@ curl http://localhost:5000/api
## Regândirea gestionării stării
În [lecția anterioară](../3-data/README.md), am introdus un concept de bază al stării în aplicația noastră cu variabila globală `account`, care conține datele bancare ale utilizatorului conectat în prezent. Totuși, implementarea noastră actuală are unele defecte. Încearcă să reîmprospătezi pagina când ești pe tabloul de bord. Ce se întâmplă?
În [lecția anterioară](../3-data/README.md), am introdus un concept de bază al stării în aplicația noastră cu variabila globală `account`, care conține datele bancare ale utilizatorului conectat în prezent. Totuși, implementarea noastră actuală are câteva defecte. Încercați să reîmprospătați pagina când sunteți pe tabloul de bord. Ce se întâmplă?
Există 3 probleme cu codul actual:
- Starea nu este păstrată, deoarece o reîmprospătare a browserului te duce înapoi la pagina de autentificare.
- Există mai multe funcții care modifică starea. Pe măsură ce aplicația crește, poate deveni dificil să urmărești modificările și este ușor să uiți să actualizezi una.
- Starea nu este curățată, astfel încât atunci când dai clic pe *Logout*, datele contului sunt încă acolo, deși ești pe pagina de autentificare.
- Starea nu este păstrată, deoarece o reîmprospătare a browserului vă duce înapoi la pagina de autentificare.
- Există mai multe funcții care modifică starea. Pe măsură ce aplicația crește, acest lucru poate face dificilă urmărirea modificărilor și este ușor să uitați să actualizați una.
- Starea nu este curățată, astfel încât atunci când faceți clic pe *Logout*, datele contului sunt încă acolo, deși sunteți pe pagina de autentificare.
Am putea actualiza codul nostru pentru a aborda aceste probleme una câte una, dar acest lucru ar crea mai multă duplicare de cod și ar face aplicația mai complexă și dificil de întreținut. Sau am putea să ne oprim câteva minute și să ne regândim strategia.
Am putea actualiza codul nostru pentru a aborda aceste probleme una câte una, dar acest lucru ar crea mai multă duplicare a codului și ar face aplicația mai complexă și dificil de întreținut. Sau am putea să ne oprim câteva minute și să ne regândim strategia.
> Ce probleme încercăm cu adevărat să rezolvăm aici?
[Gestionarea stării](https://en.wikipedia.org/wiki/State_management) se referă la găsirea unei abordări bune pentru a rezolva aceste două probleme particulare:
- Cum să păstrăm fluxurile de date dintr-o aplicație ușor de înțeles?
- Cum să menținem datele stării mereu sincronizate cu interfața utilizatorului (și viceversa)?
- Cum să păstrăm datele stării mereu sincronizate cu interfața utilizatorului (și viceversa)?
Odată ce ai rezolvat aceste probleme, orice alte probleme pe care le-ai putea avea fie sunt deja rezolvate, fie au devenit mai ușor de rezolvat. Există multe abordări posibile pentru rezolvarea acestor probleme, dar vom merge cu o soluție comună care constă în **centralizarea datelor și a modurilor de a le modifica**. Fluxurile de date ar arăta astfel:
Odată ce ați rezolvat aceste probleme, orice alte probleme pe care le-ați putea avea fie sunt deja rezolvate, fie au devenit mai ușor de rezolvat. Există multe abordări posibile pentru rezolvarea acestor probleme, dar vom merge cu o soluție comună care constă în **centralizarea datelor și a modurilor de a le modifica**. Fluxurile de date ar arăta astfel:

> Nu vom acoperi aici partea în care datele declanșează automat actualizarea vizualizării, deoarece este legată de concepte mai avansate de [Programare Reactivă](https://en.wikipedia.org/wiki/Reactive_programming). Este un subiect bun de aprofundare dacă ești interesat.
> Nu vom acoperi aici partea în care datele declanșează automat actualizarea vizualizării, deoarece este legată de concepte mai avansate de [Programare Reactivă](https://en.wikipedia.org/wiki/Reactive_programming). Este un subiect bun de aprofundare dacă sunteți interesat.
✅ Există multe biblioteci care oferă abordări diferite pentru gestionarea stării, [Redux](https://redux.js.org) fiind o opțiune populară. Aruncă o privire asupra conceptelor și modelelor utilizate, deoarece este adesea o modalitate bună de a învăța ce probleme potențiale ai putea întâmpina în aplicațiile web mari și cum pot fi rezolvate.
✅ Există multe biblioteci care oferă abordări diferite pentru gestionarea stării, [Redux](https://redux.js.org) fiind o opțiune populară. Aruncați o privire asupra conceptelor și modelelor utilizate, deoarece este adesea o modalitate bună de a învăța ce probleme potențiale puteți întâmpina în aplicațiile web mari și cum pot fi rezolvate.
### Sarcină
Vom începe cu puțină refactorizare. Înlocuiește declarația `account`:
Vom începe cu puțină refactorizare. Înlocuiți declarația `account`:
```js
let account = null;
@ -77,9 +77,9 @@ let state = {
Ideea este să *centralizăm* toate datele aplicației noastre într-un singur obiect de stare. Deocamdată avem doar `account` în stare, deci nu se schimbă prea mult, dar creează o cale pentru evoluții.
De asemenea, trebuie să actualizăm funcțiile care îl folosesc. În funcțiile `register()` și `login()`, înlocuiește`account = ...` cu `state.account = ...`;
De asemenea, trebuie să actualizăm funcțiile care îl folosesc. În funcțiile `register()` și `login()`, înlocuiți`account = ...` cu `state.account = ...`;
La începutul funcției `updateDashboard()`, adaugă această linie:
La începutul funcției `updateDashboard()`, adăugați această linie:
```js
const account = state.account;
@ -91,11 +91,11 @@ Această refactorizare în sine nu a adus multe îmbunătățiri, dar ideea a fo
Acum că am pus în aplicare obiectul `state` pentru a stoca datele noastre, următorul pas este să centralizăm actualizările. Scopul este să fie mai ușor să urmărim orice modificări și când se întâmplă.
Pentru a evita modificările făcute obiectului `state`, este de asemenea o practică bună să îl considerăm [*imutabil*](https://en.wikipedia.org/wiki/Immutable_object), ceea ce înseamnă că nu poate fi modificat deloc. De asemenea, înseamnă că trebuie să creezi un nou obiect de stare dacă vrei să schimbi ceva în el. Procedând astfel, construiești o protecție împotriva efectelor secundare [nedorite](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) și deschizi posibilități pentru noi funcționalități în aplicația ta, cum ar fi implementarea funcțiilor de anulare/refacere, în timp ce faci mai ușor de depanat. De exemplu, ai putea înregistra fiecare modificare făcută stării și păstra un istoric al modificărilor pentru a înțelege sursa unui bug.
Pentru a evita modificările făcute obiectului `state`, este de asemenea o practică bună să îl considerăm [*imobil*](https://en.wikipedia.org/wiki/Immutable_object), ceea ce înseamnă că nu poate fi modificat deloc. De asemenea, înseamnă că trebuie să creați un nou obiect de stare dacă doriți să schimbați ceva în el. Procedând astfel, construiți o protecție împotriva [efectelor secundare](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) nedorite și deschideți posibilități pentru noi funcționalități în aplicația dvs., cum ar fi implementarea funcțiilor de anulare/refacere, în timp ce faceți mai ușor de depanat. De exemplu, ați putea înregistra fiecare modificare făcută stării și păstra un istoric al modificărilor pentru a înțelege sursa unei erori.
În JavaScript, poți folosi [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) pentru a crea o versiune imutabilă a unui obiect. Dacă încerci să faci modificări unui obiect imutabil, va fi generată o excepție.
În JavaScript, puteți utiliza [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) pentru a crea o versiune imobilă a unui obiect. Dacă încercați să faceți modificări unui obiect imobil, va fi generată o excepție.
✅ Știi diferența dintre un obiect imutabil *superficial* și unul *profund*? Poți citi despre asta [aici](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Știți diferența dintre un obiect imobil *superficial* și unul *profund*? Puteți citi despre asta [aici](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Sarcină
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
În această funcție, creăm un nou obiect de stare și copiem datele din starea anterioară folosind [operatorul *spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Apoi suprascriem o anumită proprietate a obiectului de stare cu noile date folosind [notația cu paranteze](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` pentru atribuire. În final, blocăm obiectul pentru a preveni modificările folosind `Object.freeze()`. Deocamdată avem doar proprietatea `account` stocată în stare, dar cu această abordare poți adăuga câte proprietăți dorești în stare.
În această funcție, creăm un nou obiect de stare și copiem datele din starea anterioară folosind [operatorul *spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Apoi suprascriem o anumită proprietate a obiectului de stare cu noile date folosind [notația cu paranteze](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` pentru atribuire. În final, blocăm obiectul pentru a preveni modificările folosind `Object.freeze()`. Deocamdată avem doar proprietatea `account` stocată în stare, dar cu această abordare puteți adăuga câte proprietăți aveți nevoie în stare.
Vom actualiza și inițializarea `state` pentru a ne asigura că starea inițială este și ea înghețată:
Vom actualiza și inițializarea `state` pentru a ne asigura că starea inițială este de asemenea blocată:
```js
let state = Object.freeze({
@ -120,13 +120,13 @@ let state = Object.freeze({
});
```
După aceea, actualizează funcția `register` înlocuind atribuirea `state.account = result;` cu:
După aceea, actualizați funcția `register` înlocuind atribuirea `state.account = result;` cu:
```js
updateState('account', result);
```
Fă același lucru cu funcția `login`, înlocuind `state.account = data;` cu:
Faceți același lucru cu funcția `login`, înlocuind `state.account = data;` cu:
```js
updateState('account', data);
@ -134,7 +134,7 @@ updateState('account', data);
Vom profita acum de ocazie pentru a rezolva problema datelor contului care nu sunt șterse atunci când utilizatorul face clic pe *Logout*.
Creează o nouă funcție `logout()`:
Creați o nouă funcție `logout()`:
```js
function logout() {
@ -143,47 +143,47 @@ function logout() {
}
```
În `updateDashboard()`, înlocuiește redirecționarea `return navigate('/login');` cu `return logout();`
În `updateDashboard()`, înlocuiți redirecționarea `return navigate('/login');` cu `return logout()`;
Încearcă să înregistrezi un nou cont, să te deconectezi și să te conectezi din nou pentru a verifica dacă totul funcționează corect.
Încercați să înregistrați un nou cont, să vă deconectați și să vă conectați din nou pentru a verifica dacă totul funcționează corect.
> Sfat: poți analiza toate modificările stării adăugând `console.log(state)` la sfârșitul funcției `updateState()` și deschizând consola în instrumentele de dezvoltare ale browserului.
> Sfat: puteți analiza toate modificările stării adăugând `console.log(state)` la sfârșitul funcției `updateState()` și deschizând consola în instrumentele de dezvoltare ale browserului.
## Păstrarea stării
Majoritatea aplicațiilor web trebuie să păstreze date pentru a funcționa corect. Toate datele critice sunt de obicei stocate într-o bază de date și accesate printr-un server API, cum ar fi datele contului utilizatorului în cazul nostru. Dar uneori, este interesant să păstrezi unele date în aplicația client care rulează în browser, pentru o experiență mai bună a utilizatorului sau pentru a îmbunătăți performanța la încărcare.
Majoritatea aplicațiilor web trebuie să păstreze datele pentru a funcționa corect. Toate datele critice sunt de obicei stocate într-o bază de date și accesate printr-un API de server, cum ar fi datele contului utilizatorului în cazul nostru. Dar uneori, este de asemenea interesant să păstrăm unele date în aplicația client care rulează în browser, pentru o experiență mai bună a utilizatorului sau pentru a îmbunătăți performanța la încărcare.
Când vrei să păstrezi date în browser, există câteva întrebări importante pe care ar trebui să le pui:
Când doriți să păstrați datele în browser, există câteva întrebări importante pe care ar trebui să vi le puneți:
- *Sunt datele sensibile?* Ar trebui să eviți stocarea oricăror date sensibile pe client, cum ar fi parolele utilizatorului.
- *Pentru cât timp ai nevoie să păstrezi aceste date?* Plănuiești să accesezi aceste date doar pentru sesiunea curentă sau vrei să fie stocate permanent?
- *Sunt datele sensibile?* Ar trebui să evitați stocarea oricăror date sensibile pe client, cum ar fi parolele utilizatorului.
- *Pentru cât timp aveți nevoie să păstrați aceste date?* Plănuiți să accesați aceste date doar pentru sesiunea curentă sau doriți să fie stocate permanent?
Există mai multe modalități de a stoca informații într-o aplicație web, în funcție de ceea ce vrei să realizezi. De exemplu, poți folosi URL-urile pentru a stoca o interogare de căutare și a o face partajabilă între utilizatori. De asemenea, poți folosi [cookie-uri HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) dacă datele trebuie să fie partajate cu serverul, cum ar fi informațiile de [autentificare](https://en.wikipedia.org/wiki/Authentication).
Există mai multe modalități de a stoca informații într-o aplicație web, în funcție de ceea ce doriți să realizați. De exemplu, puteți utiliza URL-urile pentru a stoca o interogare de căutare și a o face partajabilă între utilizatori. De asemenea, puteți utiliza [cookie-uri HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) dacă datele trebuie să fie partajate cu serverul, cum ar fi informațiile de [autentificare](https://en.wikipedia.org/wiki/Authentication).
O altă opțiune este să folosești una dintre numeroasele API-uri ale browserului pentru stocarea datelor. Două dintre ele sunt deosebit de interesante:
O altă opțiune este să utilizați una dintre numeroasele API-uri ale browserului pentru stocarea datelor. Două dintre ele sunt deosebit de interesante:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): un [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) care permite păstrarea datelor specifice site-ului web curent între diferite sesiuni. Datele salvate în acesta nu expiră niciodată.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funcționează la fel ca `localStorage`, cu excepția faptului că datele stocate în acesta sunt șterse când sesiunea se încheie (când browserul este închis).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): acesta funcționează la fel ca `localStorage`, cu excepția faptului că datele stocate în acesta sunt șterse când sesiunea se termină (când browserul este închis).
Reține că ambele API-uri permit doar stocarea de [șiruri de caractere](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Dacă vrei să stochezi obiecte complexe, va trebui să le serializezi în formatul [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) folosind [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Rețineți că ambele API-uri permit doar stocarea [șirurilor de caractere](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Dacă doriți să stocați obiecte complexe, va trebui să le serializați în formatul [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) folosind [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Dacă vrei să creezi o aplicație web care nu funcționează cu un server, este posibil să creezi o bază de date pe client folosind API-ul [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Acesta este rezervat pentru cazuri de utilizare avansate sau dacă ai nevoie să stochezi o cantitate semnificativă de date, deoarece este mai complex de utilizat.
✅ Dacă doriți să creați o aplicație web care nu funcționează cu un server, este de asemenea posibil să creați o bază de date pe client folosind API-ul [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Acesta este rezervat pentru cazuri de utilizare avansate sau dacă aveți nevoie să stocați o cantitate semnificativă de date, deoarece este mai complex de utilizat.
### Sarcină
Ne dorim ca utilizatorii noștri să rămână conectați până când apasă explicit butonul *Logout*, așa că vom folosi `localStorage` pentru a stoca datele contului. Mai întâi, să definim o cheie pe care o vom folosi pentru a stoca datele noastre.
Ne dorim ca utilizatorii noștri să rămână conectați până când fac clic explicit pe butonul *Logout*, așa că vom folosi `localStorage` pentru a stoca datele contului. Mai întâi, să definim o cheie pe care o vom folosi pentru a stoca datele noastre.
```js
const storageKey = 'savedAccount';
```
Apoi adaugă această linie la sfârșitul funcției `updateState()`:
Apoi adăugați această linie la sfârșitul funcției `updateState()`:
Cu aceasta, datele contului utilizatorului vor fi păstrate și mereu actualizate, deoarece am centralizat anterior toate actualizările stării. Aici începem să beneficiem de toate refactorizările anterioare 🙂.
Cu aceasta, datele contului utilizatorului vor fi păstrate și mereu actualizate, deoarece am centralizat anterior toate actualizările stării. Acesta este momentul în care începem să beneficiem de toate refactorizările anterioare 🙂.
Deoarece datele sunt salvate, trebuie să ne ocupăm și de restaurarea lor atunci când aplicația este încărcată. Deoarece vom începe să avem mai mult cod de inițializare, ar putea fi o idee bună să creăm o nouă funcție `init`, care include și codul nostru anterior de la sfârșitul `app.js`:
@ -202,17 +202,17 @@ function init() {
init();
```
Aici recuperăm datele salvate, iar dacă există, actualizăm starea corespunzător. Este important să facem acest lucru *înainte* de a actualiza ruta, deoarece ar putea exista cod care se bazează pe stare în timpul actualizării paginii.
Aici recuperăm datele salvate, iar dacă există, actualizăm starea în consecință. Este important să facem acest lucru *înainte* de actualizarea rutei, deoarece ar putea exista cod care se bazează pe stare în timpul actualizării paginii.
De asemenea, putem face pagina *Dashboard* pagina implicită a aplicației noastre, deoarece acum păstrăm datele contului. Dacă nu se găsesc date, tabloul de bord se ocupă de redirecționarea către pagina de *Login*. În `updateRoute()`, înlocuiește fallback-ul `return navigate('/login');` cu `return navigate('/dashboard');`.
De asemenea, putem face pagina *Dashboard* pagina implicită a aplicației noastre, deoarece acum păstrăm datele contului. Dacă nu se găsesc date, tabloul de bord se ocupă de redirecționarea către pagina *Login*. În `updateRoute()`, înlocuiți fallback-ul `return navigate('/login');` cu `return navigate('/dashboard');`.
Acum conectează-te în aplicație și încearcă să reîmprospătezi pagina. Ar trebui să rămâi pe tabloul de bord. Cu această actualizare, am rezolvat toate problemele inițiale...
Acum conectați-vă în aplicație și încercați să reîmprospătați pagina. Ar trebui să rămâneți pe tabloul de bord. Cu această actualizare, am rezolvat toate problemele inițiale...
## Reîmprospătarea datelor
...Dar s-ar putea să fi creat și o nouă problemă. Ups!
Accesează tabloul de bord folosind contul `test`, apoi rulează această comandă într-un terminal pentru a crea o nouă tranzacție:
Accesați tabloul de bord folosind contul `test`, apoi rulați această comandă într-un terminal pentru a crea o nouă tranzacție:
Încearcă să reîmprospătezi pagina tabloului de bord în browser acum. Ce se întâmplă? Vezi noua tranzacție?
Încercați să reîmprospătați pagina tabloului de bord în browser acum. Ce se întâmplă? Vedeți noua tranzacție?
Starea este păstrată pe termen nelimitat datorită `localStorage`, dar asta înseamnă și că nu este niciodată actualizată până când nu te deconectezi din aplicație și te conectezi din nou!
Starea este păstrată pe termen nelimitat datorită `localStorage`, dar asta înseamnă și că nu este niciodată actualizată până când nu vă deconectați din aplicație și vă conectați din nou!
O strategie posibilă pentru a rezolva acest lucru este să reîncărcăm datele contului de fiecare dată când tabloul de bord este încărcat, pentru a evita datele învechite.
### Sarcină
Creează o nouă funcție `updateAccountData`:
Creați o nouă funcție `updateAccountData`:
```js
async function updateAccountData() {
@ -249,7 +249,7 @@ async function updateAccountData() {
Această metodă verifică dacă suntem conectați în prezent, apoi reîncarcă datele contului de pe server.
Creează o altă funcție numită `refresh`:
Creați o altă funcție numită `refresh`:
```js
async function refresh() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
Aceasta actualizează datele contului, apoi se ocupă de actualizarea HTML-ului paginii tabloului de bord. Este ceea ce trebuie să apelăm atunci când ruta tabloului de bord este încărcată. Actualizează definiția rutei cu:
Aceasta actualizează datele contului, apoi se ocupă de actualizarea HTML-ului paginii tabloului de bord. Este ceea ce trebuie să apelăm atunci când ruta tabloului de bord este încărcată. Actualizați definiția rutei cu:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
Încearcă să reîmprospătezi tabloul de bord acum, ar trebui să afișeze datele contului actualizate.
Încercați să reîmprospătați tabloul de bord acum, ar trebui să afișeze datele contului actualizate.
---
## 🚀 Provocare
Acum că reîncărcăm datele contului de fiecare dată când tabloul de bord este încărcat, crezi că mai avem nevoie să păstrăm *toate datele contului*?
Acum că reîncărcăm datele contului de fiecare dată când tabloul de bord este încărcat, credeți că mai trebuie să păstrăm *toate datele contului*?
Încearcă să lucrați împreună pentru a schimba ceea ce este salvat și încărcat din `localStorage` astfel încât să includă doar ceea ce este absolut necesar pentru ca aplicația să funcționeze.
Încercați să lucrați împreună pentru a schimba ceea ce este salvat și încărcat din `localStorage` astfel încât să includă doar ceea ce este absolut necesar pentru ca aplicația să funcționeze.
## Chestionar după lecție
[Chestionar după lecție](https://ff-quizzes.netlify.app/web/quiz/48)
Iată un exemplu de rezultat după finalizarea sarcinii:
Iată un exemplu de rezultat după finalizarea temei:

---
**Declinarea responsabilității**:
Acest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși depunem eforturi pentru a asigura acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa nativă ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm răspunderea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.
Acest document a fost tradus utilizând serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși depunem eforturi pentru acuratețe, vă rugăm să aveți în vedere că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa nativă trebuie considerat sursa autoritară. Pentru informații critice, se recomandă traducerea realizată de un profesionist uman. Nu ne asumăm răspunderea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.
[Тест перед лекцией](https://ff-quizzes.netlify.app/web/quiz/47)
### Введение
По мере роста веб-приложения становится сложнее отслеживать все потоки данных. Какой код получает данные, какая страница их использует, где и когда их нужно обновить... Легко запутаться и получить код, который сложно поддерживать. Это особенно актуально, если нужно делиться данными между различными страницами приложения, например, данными пользователя. Концепция *управления состоянием* всегда существовала во всех типах программ, но с увеличением сложности веб-приложений она стала ключевым моментом, который нужно учитывать при разработке.
По мере роста веб-приложения становится все сложнее отслеживать потоки данных. Какой код получает данные, какая страница их использует, где и когда их нужно обновлять... Легко запутаться в коде, который становится трудно поддерживать. Это особенно актуально, когда нужно делиться данными между разными страницами приложения, например, пользовательскими данными. Концепция *управления состоянием* всегда существовала во всех видах программ, но с ростом сложности веб-приложений она стала ключевым моментом, который необходимо учитывать при разработке.
В этой заключительной части мы пересмотрим наше приложение, чтобы улучшить управление состоянием, обеспечив поддержку обновления браузера в любой момент и сохранение данных между сеансами пользователя.
В этой заключительной части мы пересмотрим приложение, которое мы создали, чтобы улучшить управление состоянием, добавив поддержку обновления браузера в любой момент и сохранение данных между пользовательскими сессиями.
### Предварительные требования
Для этого урока вам нужно завершить часть веб-приложения, посвященную [получению данных](../3-data/README.md). Также необходимо установить [Node.js](https://nodejs.org) и [запустить сервер API](../api/README.md) локально, чтобы управлять данными учетной записи.
Для этого урока вам нужно завершить часть веб-приложения, посвященную [извлечению данных](../3-data/README.md). Также необходимо установить [Node.js](https://nodejs.org) и [запустить серверный API](../api/README.md) локально, чтобы управлять данными учетной записи.
Вы можете проверить, что сервер работает корректно, выполнив эту команду в терминале:
Вы можете проверить, правильно ли работает сервер, выполнив эту команду в терминале:
```sh
curl http://localhost:5000/api
@ -34,32 +34,32 @@ curl http://localhost:5000/api
## Пересмотр управления состоянием
В [предыдущем уроке](../3-data/README.md) мы ввели базовую концепцию состояния в нашем приложении с помощью глобальной переменной `account`, которая содержит банковские данные текущего пользователя. Однако наша текущая реализация имеет несколько недостатков. Попробуйте обновить страницу, находясь на панели управления. Что происходит?
В [предыдущем уроке](../3-data/README.md) мы ввели базовое понятие состояния в нашем приложении с помощью глобальной переменной `account`, которая содержит банковские данные текущего авторизованного пользователя. Однако в текущей реализации есть недостатки. Попробуйте обновить страницу, находясь на панели управления. Что происходит?
Есть три проблемы с текущим кодом:
В текущем коде есть три проблемы:
- Состояние не сохраняется, так как обновление браузера возвращает вас на страницу входа.
- Существует несколько функций, которые изменяют состояние. По мере роста приложения это может затруднить отслеживание изменений, и легко забыть обновить одну из них.
- Состояние не очищается, поэтому при нажатии на *Выход* данные учетной записи остаются, даже если вы находитесь на странице входа.
- Состояние не сохраняется: при обновлении страницы вы возвращаетесь на страницу входа.
- Существует несколько функций, которые изменяют состояние. По мере роста приложения это может затруднить отслеживание изменений, и легко забыть обновить что-то одно.
- Состояние не очищается: когда вы нажимаете *Выйти*, данные учетной записи остаются, даже если вы находитесь на странице входа.
Мы могли бы обновить наш код, чтобы решить эти проблемы по отдельности, но это привело бы к дублированию кода и усложнило бы приложение. Или мы могли бы остановиться на несколько минут и пересмотреть нашу стратегию.
> Какие проблемы мы действительно пытаемся решить?
[Управление состоянием](https://en.wikipedia.org/wiki/State_management) заключается в поиске хорошего подхода для решения двух конкретных проблем:
[Управление состоянием](https://en.wikipedia.org/wiki/State_management) заключается в поиске подходящего подхода для решения двух основных проблем:
- Как сделать потоки данных в приложении понятными?
- Как обеспечить синхронность данных состояния с пользовательским интерфейсом (и наоборот)?
- Как обеспечить синхронизацию данных состояния с пользовательским интерфейсом (и наоборот)?
После того как вы решите эти проблемы, любые другие возникающие вопросы либо уже будут решены, либо станут проще для решения. Существует множество подходов к решению этих проблем, но мы выберем распространенное решение, которое заключается в **централизации данных и способов их изменения**. Потоки данных будут выглядеть следующим образом:

> Мы не будем здесь рассматривать часть, где данные автоматически вызывают обновление представления, так как это связано с более сложными концепциями [реактивного программирования](https://en.wikipedia.org/wiki/Reactive_programming). Это хороший предмет для дальнейшего изучения, если вы готовы к глубокому погружению.
> Здесь мы не будем рассматривать часть, где данные автоматически обновляют представление, так как это связано с более сложными концепциями [реактивного программирования](https://en.wikipedia.org/wiki/Reactive_programming). Это хорошая тема для дальнейшего изучения, если вы хотите углубиться.
✅ Существует множество библиотек с различными подходами к управлению состоянием, [Redux](https://redux.js.org) является популярным вариантом. Ознакомьтесь с концепциями и шаблонами, которые используются, так как это часто помогает понять потенциальные проблемы, с которыми вы можете столкнуться в крупных веб-приложениях, и способы их решения.
✅ Существует множество библиотек с разными подходами к управлению состоянием, [Redux](https://redux.js.org) — один из популярных вариантов. Ознакомьтесь с концепциями и шаблонами, которые используются, так как это часто помогает понять потенциальные проблемы, с которыми вы можете столкнуться в крупных веб-приложениях, и способы их решения.
### Задача
### Задание
Начнем с небольшого рефакторинга. Замените объявление `account`:
@ -75,7 +75,7 @@ let state = {
};
```
Идея заключается в *централизации* всех данных нашего приложения в одном объекте состояния. Пока у нас есть только `account` в состоянии, поэтому это не сильно меняет ситуацию, но создает основу для дальнейших изменений.
Идея состоит в том, чтобы *централизовать* все данные нашего приложения в одном объекте состояния. Пока у нас есть только `account` в состоянии, поэтому это не сильно изменит ситуацию, но создаст основу для дальнейших изменений.
Также нужно обновить функции, которые его используют. В функциях `register()` и `login()` замените `account = ...` на `state.account = ...`;
@ -85,21 +85,21 @@ let state = {
const account = state.account;
```
Этот рефакторинг сам по себе не принес больших улучшений, но его идея заключалась в создании основы для следующих изменений.
Сам по себе этот рефакторинг не принес больших улучшений, но его идея — заложить основу для следующих изменений.
## Отслеживание изменений данных
Теперь, когда мы создали объект `state` для хранения данных, следующим шагом будет централизация обновлений. Цель — упростить отслеживание любых изменений и времени их возникновения.
Чтобы избежать изменений объекта `state`, также рекомендуется считать его [*неизменяемым*](https://en.wikipedia.org/wiki/Immutable_object), то есть таким, который нельзя модифицировать. Это также означает, что вам нужно создать новый объект состояния, если вы хотите что-то изменить в нем. Таким образом, вы защищаете себя от потенциально нежелательных [побочных эффектов](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) и открываете возможности для новых функций в вашем приложении, таких как реализация отмены/повтора, а также упрощаете отладку. Например, вы можете записывать каждое изменение состояния и сохранять историю изменений, чтобы понять источник ошибки.
Чтобы избежать изменений в объекте `state`, также полезно рассматривать его как [*неизменяемый*](https://en.wikipedia.org/wiki/Immutable_object), то есть такой, который нельзя изменить. Это также означает, что для изменения чего-либо в нем нужно создать новый объект состояния. Такой подход защищает от нежелательных [побочных эффектов](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) и открывает возможности для новых функций в приложении, таких как реализация отмены/повтора действий, а также упрощает отладку. Например, вы могли бы записывать каждое изменение состояния и сохранять историю изменений, чтобы понять источник ошибки.
В JavaScript вы можете использовать [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) для создания неизменяемой версии объекта. Если вы попытаетесь внести изменения в неизменяемый объект, будет вызвано исключение.
В JavaScript можно использовать [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) для создания неизменяемой версии объекта. Если вы попытаетесь внести изменения в неизменяемый объект, будет вызвано исключение.
✅ Знаете ли вы разницу между *поверхностным* и *глубоким* неизменяемым объектом? Вы можете прочитать об этом [здесь](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Задача
### Задание
Создадим новую функцию `updateState()`:
Давайте создадим новую функцию `updateState()`:
```js
function updateState(property, newData) {
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
В этой функции мы создаем новый объект состояния и копируем данные из предыдущего состояния, используя [*оператор распространения (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Затем мы переопределяем конкретное свойство объекта состояния новыми данными, используя [нотацию скобок](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` для присваивания. Наконец, мы блокируем объект, чтобы предотвратить модификации, используя`Object.freeze()`. Пока у нас есть только свойство `account`, хранящееся в состоянии, но с этим подходом вы можете добавлять столько свойств, сколько вам нужно.
В этой функции мы создаем новый объект состояния и копируем данные из предыдущего состояния, используя [*оператор распространения (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Затем мы переопределяем конкретное свойство объекта состояния новыми данными, используя [скобочную нотацию](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` для присвоения. Наконец, мы блокируем объект, чтобы предотвратить его изменения, с помощью`Object.freeze()`. Пока у нас есть только свойство `account` в состоянии, но с этим подходом вы можете добавлять столько свойств, сколько нужно.
Также обновим инициализацию `state`, чтобы убедиться, что начальное состояние тоже заморожено:
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
После этого обновите функцию `register`, заменив присваивание `state.account = result;` на:
После этого обновите функцию `register`, заменив присвоение `state.account = result;` на:
Теперь воспользуемся случаем, чтобы исправить проблему, связанную с тем, что данные учетной записи не очищаются, когда пользователь нажимает на *Выход*.
Теперь воспользуемся случаем, чтобы исправить проблемус данными учетной записи, которые не очищаются, когда пользователь нажимает на *Выйти*.
Создайте новую функцию `logout()`:
@ -143,35 +143,35 @@ function logout() {
}
```
В`updateDashboard()` замените перенаправление `return navigate('/login');` на `return logout();`
В`updateDashboard()` замените перенаправление `return navigate('/login');` на `return logout();`;
Попробуйте зарегистрировать новую учетную запись, выйти из системы и снова войти, чтобы убедиться, что все работает корректно.
Попробуйте зарегистрировать новую учетную запись, выйти и снова войти, чтобы убедиться, что все работает правильно.
> Совет: вы можете посмотреть все изменения состояния, добавив `console.log(state)` в конце`updateState()` и открыв консоль в инструментах разработчика вашего браузера.
> Совет: вы можете посмотреть все изменения состояния, добавив `console.log(state)` в конец`updateState()` и открыв консоль в инструментах разработчика вашего браузера.
## Сохранение состояния
Большинство веб-приложений нуждаются в сохранении данных для корректной работы. Все критические данные обычно хранятся в базе данных и доступны через серверный API, как, например, данные учетной записи пользователя в нашем случае. Но иногда также полезно сохранять некоторые данные на клиенте, который работает в вашем браузере, для улучшения пользовательского опыта или повышения производительности загрузки.
Большинство веб-приложений нуждаются в сохранении данных для корректной работы. Все критические данные обычно хранятся в базе данных и доступны через серверный API, как в случае с данными учетной записи пользователя. Но иногда также полезно сохранять некоторые данные на клиентском приложении, работающем в вашем браузере, чтобы улучшить пользовательский опыт или повысить производительность загрузки.
Когда вы хотите сохранить данные в браузере, есть несколько важных вопросов, которые следует задать:
Когда вы хотите сохранить данные в браузере, важно задать себе несколько вопросов:
- *Являются ли данные конфиденциальными?* Следует избегать хранения любых конфиденциальных данных на клиенте, таких как пароли пользователей.
- *Как долго вам нужно хранить эти данные?* Планируете ли вы использовать эти данные только в текущем сеансе или хотите, чтобы они хранились навсегда?
- *Являются ли данные конфиденциальными?* Следует избегать хранения конфиденциальных данных на клиенте, таких как пароли пользователей.
- *Как долго вам нужно хранить эти данные?* Планируете ли вы использовать эти данные только в текущей сессии или хотите сохранить их навсегда?
Существует несколько способов хранения информации внутри веб-приложения, в зависимости от того, что вы хотите достичь. Например, вы можете использовать URL-адреса для хранения поискового запроса и сделать его доступным для других пользователей. Вы также можете использовать [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), если данные нужно передавать на сервер, например, информацию для [аутентификации](https://en.wikipedia.org/wiki/Authentication).
Существует несколько способов хранения информации внутри веб-приложения, в зависимости от ваших целей. Например, вы можете использовать URL-адреса для хранения поисковых запросов и сделать их доступными для других пользователей. Вы также можете использовать [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), если данные нужно передавать на сервер, например, для [аутентификации](https://en.wikipedia.org/wiki/Authentication).
Другой вариант — использовать один из многих API браузера для хранения данных. Два из них особенно интересны:
Еще один вариант — использовать один из многих API браузера для хранения данных. Два из них особенно интересны:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [хранилище ключ/значение](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), позволяющее сохранять данные, специфичные для текущего веб-сайта, между различными сеансами. Сохраненные данные никогда не истекают.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): работает так же, как `localStorage`, за исключением того, что данные, хранящиеся в нем, очищаются при завершении сеанса (при закрытии браузера).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [хранилище ключ/значение](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), позволяющее сохранять данные, специфичные для текущего веб-сайта, между сессиями. Сохраненные данные никогда не истекают.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): работает так же, как `localStorage`, за исключением того, что данные очищаются, когда сессия завершается (при закрытии браузера).
Обратите внимание, что оба этих API позволяют хранить только [строки](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Если вы хотите хранить сложные объекты, вам нужно сериализовать их в формате [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) с помощью [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Обратите внимание, что оба этих API позволяют хранить только [строки](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Если вы хотите сохранить сложные объекты, вам нужно сериализовать их в формате [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) с помощью [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Если вы хотите создать веб-приложение, которое не работает с сервером, также можно создать базу данных на клиенте, используя API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Этот вариант предназначен для сложных случаев использования или если вам нужно хранить значительное количество данных, так как он более сложен в использовании.
✅ Если вы хотите создать веб-приложение, которое работает без сервера, также можно создать базу данных на клиенте, используя API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Этот вариант подходит для сложных случаев или если вам нужно хранить значительное количество данных, так как он более сложен в использовании.
### Задача
### Задание
Мы хотим, чтобы наши пользователи оставались в системе, пока они явно не нажмут кнопку *Выход*, поэтому будем использовать `localStorage` для хранения данных учетной записи. Сначала определим ключ, который будем использовать для хранения данных.
Мы хотим, чтобы наши пользователи оставались авторизованными, пока они явно не нажмут кнопку *Выйти*, поэтому будем использовать `localStorage` для хранения данных учетной записи. Сначала определим ключ, который будем использовать для хранения данных.
С этим данные учетной записи пользователя будут сохраняться и всегда актуальны, так как мы централизовали все обновления состояния. Здесь мы начинаем получать выгоду от всех предыдущих рефакторингов 🙂.
С этим пользовательские данные учетной записи будут сохраняться и всегда актуальны, так как мы централизовали все обновления состояния. Именно здесь мы начинаем получать выгоду от всех предыдущих рефакторингов 🙂.
Так как данные сохраняются, также нужно позаботиться об их восстановлении при загрузке приложения. Поскольку у нас начинает появляться больше кода для инициализации, может быть хорошей идеей создать новую функцию `init`, которая также включает наш предыдущий код внизу`app.js`:
Поскольку данные сохраняются, также нужно позаботиться об их восстановлении при загрузке приложения. Поскольку у нас начнет появляться больше кода инициализации, возможно, стоит создать новую функцию `init`, которая также включает наш предыдущий код в конце`app.js`:
```js
function init() {
@ -202,15 +202,15 @@ function init() {
init();
```
Здесь мы восстанавливаем сохраненные данные, и если они есть, обновляем состояние соответствующим образом. Важно сделать это *до* обновления маршрута, так как может быть код, который зависит от состояния во время обновления страницы.
Здесь мы извлекаем сохраненные данные, и если они есть, обновляем состояние соответствующим образом. Важно сделать это *до* обновления маршрута, так как может быть код, зависящий от состояния во время обновления страницы.
Мы также можем сделать страницу *Панель управления* страницей по умолчанию для нашего приложения, так как теперь мы сохраняем данные учетной записи. Если данные не найдены, панель управления все равно перенаправляет на страницу *Вход*. В`updateRoute()` замените резервный вариант`return navigate('/login');` на `return navigate('/dashboard');`.
Мы также можем сделать страницу *Dashboard* страницей по умолчанию для нашего приложения, так как теперь мы сохраняем данные учетной записи. Если данные не найдены, панель управления сама перенаправляет на страницу *Login*. В`updateRoute()` замените резервное значение`return navigate('/login');` на `return navigate('/dashboard');`.
Теперь войдите в приложение и попробуйте обновить страницу. Вы должны остаться на панели управления. С этим обновлением мы решили все наши первоначальные проблемы...
Теперь войдите в приложение и попробуйте обновить страницу. Вы должны остаться на панели управления. С этим обновлением мы решили все наши начальные проблемы...
## Обновление данных
...Но мы могли также создать новую проблему. Упс!
...Но мы могли также создать новую проблему. Ой!
Перейдите на панель управления, используя учетную запись `test`, затем выполните эту команду в терминале, чтобы создать новую транзакцию:
@ -223,11 +223,11 @@ curl --request POST \
Теперь попробуйте обновить страницу панели управления в браузере. Что происходит? Видите ли вы новую транзакцию?
Состояние сохраняется бесконечно благодаря `localStorage`, но это также означает, что оно никогда не обновляется, пока вы не выйдете из приложения и снова войдете!
Состояние сохраняется на неопределенный срок благодаря `localStorage`, но это также означает, что оно никогда не обновляется, пока вы не выйдете из приложения и снова не войдете!
Один из возможных способов решения этой проблемы — перезагружать данные учетной записи каждый раз, когда загружается панель управления, чтобы избежать устаревших данных.
Одна из возможных стратегий решения этой проблемы — перезагружать данные учетной записи каждый раз, когда загружается панель управления, чтобы избежать устаревших данных.
### Задача
### Задание
Создайте новую функцию `updateAccountData`:
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Этот метод проверяет, что пользователь вошел в систему, а затем перезагружает данные учетной записи с сервера.
Этот метод проверяет, что пользователь авторизован, а затем перезагружает данные учетной записи с сервера.
Создайте еще одну функцию с именем `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Эта функция обновляет данные учетной записи, а затем обновляет HTML страницы панели управления. Это то, что нужно вызывать при загрузке маршрута панели управления. Обновите определение маршрута:
Эта функция обновляет данные учетной записи, а затем обновляет HTML панели управления. Именно ее нужно вызывать, когда загружается маршрут панели управления. Обновите определение маршрута:
```js
const routes = {
@ -273,16 +273,16 @@ const routes = {
## 🚀 Задание
Теперь, когда мы перезагружаем данные учетной записи каждый раз, когда загружается панель управления, как вы думаете, нужно ли нам сохранять *все данные учетной записи*?
Теперь, когда мы перезагружаем данные учетной записи каждый раз, когда загружается панель управления, как вы думаете, нужно ли нам по-прежнему сохранять *все данные учетной записи*?
Попробуйте вместе изменить то, что сохраняется и загружается из `localStorage`, чтобы включать только то, что абсолютно необходимо для работы приложения.
Попробуйте вместе изменить то, что сохраняется и загружается из `localStorage`, чтобы включить только то, что абсолютно необходимо для работы приложения.
Этот документ был переведен сиспользованием сервиса автоматического перевода [Co-op Translator](https://github.com/Azure/co-op-translator). Хотя мы стремимся к точности, пожалуйста, учитывайте, что автоматические переводы могут содержать ошибки или неточности. Оригинальный документ на его родном языке следует считать авторитетным источником. Для получения критически важной информации рекомендуется профессиональный перевод человеком. Мы не несем ответственности за любые недоразумения или неправильные интерпретации, возникшие в результате использования данного перевода.
Этот документ был переведен спомощью сервиса автоматического перевода [Co-op Translator](https://github.com/Azure/co-op-translator). Несмотря на наши усилия обеспечить точность перевода, автоматические переводы могут содержать ошибки или неточности. Оригинальный документ на его родном языке следует считать авторитетным источником. Для получения критически важной информации рекомендуется профессиональный перевод человеком. Мы не несем ответственности за любые недоразумения или неправильные интерпретации, возникшие в результате использования данного перевода.
Ako webová aplikácia rastie, stáva sa čoraz náročnejšie sledovať všetky toky dát. Ktorý kód získava dáta, ktorá stránka ich používa, kde a kedy je potrebné ich aktualizovať... ľahko sa dostanete k chaotickému kódu, ktorý je ťažké udržiavať. Toto je obzvlášť pravdivé, keď potrebujete zdieľať dáta medzi rôznymi stránkami aplikácie, napríklad údaje o používateľovi. Koncept *správy stavu*vždy existoval vo všetkých typoch programov, ale ako webové aplikácie rastú v komplexnosti, stáva sa kľúčovým bodom, o ktorom treba premýšľať počas vývoja.
Ako webová aplikácia rastie, stáva sa čoraz náročnejšie sledovať všetky toky dát. Ktorý kód získava dáta, ktorá stránka ich používa, kde a kedy ich treba aktualizovať... ľahko sa dostanete k neprehľadnému kódu, ktorý je ťažké udržiavať. To platí najmä vtedy, keď potrebujete zdieľať dáta medzi rôznymi stránkami aplikácie, napríklad údaje o používateľovi. Koncept *správy stavu*existoval vždy vo všetkých druhoch programov, ale ako webové aplikácie rastú na zložitosti, stáva sa kľúčovým bodom, na ktorý treba myslieť počas vývoja.
V tejto poslednej časti sa pozrieme na aplikáciu, ktorú sme vytvorili, aby sme prehodnotili, ako je spravovaný stav, čo umožní podporu obnovy prehliadača kedykoľvek a zachovanie dát medzi používateľskými reláciami.
V tejto poslednej časti sa pozrieme na aplikáciu, ktorú sme vytvorili, a prehodnotíme, ako je spravovaný stav, aby sme umožnili podporu obnovenia prehliadača kedykoľvek a zachovanie dát medzi používateľskými reláciami.
### Predpoklady
Musíte mať dokončenú [časť o získavaní dát](../3-data/README.md) webovej aplikácie pre túto lekciu. Tiež musíte nainštalovať [Node.js](https://nodejs.org) a [spustiť server API](../api/README.md) lokálne, aby ste mohli spravovať údaje o účte.
Musíte mať dokončenú časť [získavania dát](../3-data/README.md) webovej aplikácie pre túto lekciu. Tiež musíte mať nainštalovaný [Node.js](https://nodejs.org) a [spustený server API](../api/README.md) lokálne, aby ste mohli spravovať údaje o účte.
Môžete otestovať, či server správne funguje, vykonaním tohto príkazu v termináli:
Môžete otestovať, či server beží správne, spustením tohto príkazu v termináli:
```sh
curl http://localhost:5000/api
@ -34,40 +34,40 @@ curl http://localhost:5000/api
## Prehodnotenie správy stavu
V [predchádzajúcej lekcii](../3-data/README.md) sme predstavili základný koncept stavu v našej aplikácii s globálnou premennou `account`, ktorá obsahuje bankové údaje aktuálne prihláseného používateľa. Avšak naša aktuálna implementácia má niekoľko nedostatkov. Skúste obnoviť stránku, keď ste na dashboarde. Čo sa stane?
V [predchádzajúcej lekcii](../3-data/README.md) sme zaviedli základný koncept stavu v našej aplikácii s globálnou premennou `account`, ktorá obsahuje bankové údaje aktuálne prihláseného používateľa. Naša aktuálna implementácia však má niekoľko nedostatkov. Skúste obnoviť stránku, keď ste na hlavnom paneli. Čo sa stane?
Sú tu 3 problémy s aktuálnym kódom:
- Stav nie je zachovaný, pretože obnovenie prehliadača vás vráti na prihlasovaciu stránku.
- Existuje viacero funkcií, ktoré modifikujú stav. Ako aplikácia rastie, môže byť ťažké sledovať zmeny a ľahko sa zabudne na aktualizáciu jednej z nich.
- Existuje viacero funkcií, ktoré menia stav. Ako aplikácia rastie, môže byť ťažké sledovať zmeny a ľahko sa zabudne na aktualizáciu jednej z nich.
- Stav nie je vyčistený, takže keď kliknete na *Odhlásiť sa*, údaje o účte sú stále prítomné, aj keď ste na prihlasovacej stránke.
Mohli by sme aktualizovať náš kód, aby sme riešili tieto problémy jeden po druhom, ale to by vytvorilo viac duplicity kódu a urobilo aplikáciu zložitejšou a ťažšie udržiavateľnou. Alebo by sme sa mohli na chvíľu zastaviť a prehodnotiť našu stratégiu.
Mohli by sme aktualizovať náš kód, aby sme riešili tieto problémy jeden po druhom, ale to by vytvorilo viac duplicity kódu a aplikácia by bola zložitejšia a ťažšie udržiavateľná. Alebo by sme sa mohli na chvíľu zastaviť a prehodnotiť našu stratégiu.
> Aké problémy sa tu vlastne snažíme vyriešiť?
[Správa stavu](https://en.wikipedia.org/wiki/State_management) je o nájdení dobrého prístupu na riešenie týchto dvoch konkrétnych problémov:
- Ako udržať toky dát v aplikácii zrozumiteľné?
- Ako udržať údaje o stave vždy synchronizované s používateľským rozhraním (a naopak)?
- Ako udržať stavové dáta vždy synchronizované s používateľským rozhraním (a naopak)?
Keď sa postaráte o tieto problémy, akékoľvek ďalšie problémy, ktoré by ste mohli mať, môžu byť buď už vyriešené, alebo sa stanú ľahšie riešiteľnými. Existuje mnoho možných prístupov na riešenie týchto problémov, ale my sa rozhodneme pre bežné riešenie, ktoré spočíva v **centralizácii dát a spôsobov ich zmeny**. Toky dát by vyzerali takto:
Keď sa postaráte o tieto problémy, akékoľvek ďalšie problémy, ktoré by ste mohli mať, môžu byť už vyriešené alebo sa stali jednoduchšími na riešenie. Existuje mnoho možných prístupov na riešenie týchto problémov, ale my sa rozhodneme pre bežné riešenie, ktoré spočíva v **centralizácii dát a spôsobov ich zmeny**. Toky dát by vyzerali takto:

> Tu sa nebudeme zaoberať časťou, kde dáta automaticky spúšťajú aktualizáciu zobrazenia, pretože je to spojené s pokročilejšími konceptmi [Reaktívneho programovania](https://en.wikipedia.org/wiki/Reactive_programming). Je to dobrý následný predmet, ak sa chcete ponoriť hlbšie.
> Tu sa nebudeme zaoberať časťou, kde dáta automaticky spúšťajú aktualizáciu zobrazenia, pretože to súvisí s pokročilejšími konceptmi [Reaktívneho programovania](https://en.wikipedia.org/wiki/Reactive_programming). Je to dobrá téma na ďalšie štúdium, ak sa chcete ponoriť hlbšie.
✅ Existuje veľa knižníc s rôznymi prístupmi k správe stavu, pričom [Redux](https://redux.js.org) je populárnou voľbou. Pozrite sa na koncepty a vzory, ktoré používa, pretože často poskytujú dobrý spôsob, ako sa naučiť, aké potenciálne problémy môžete čeliť vo veľkých webových aplikáciách a ako ich možno vyriešiť.
✅ Existuje veľa knižníc s rôznymi prístupmi k správe stavu, pričom [Redux](https://redux.js.org) je populárnou voľbou. Pozrite sa na koncepty a vzory, ktoré používa, pretože často poskytujú dobrý prehľad o potenciálnych problémoch, ktorým môžete čeliť vo veľkých webových aplikáciách, a o tom, ako ich možno vyriešiť.
Začneme malou refaktorizáciou. Nahraďte deklaráciu `account`:
```js
let account = null;
```
S:
Nasledujúcim:
```js
let state = {
@ -75,9 +75,9 @@ let state = {
};
```
Myšlienkou je *centralizovať* všetky naše aplikačné dáta do jedného objektu stavu. Zatiaľ máme v stave iba `account`, takže sa toho veľa nemení, ale vytvára to cestu pre ďalšie rozšírenia.
Myšlienkou je *centralizovať* všetky dáta našej aplikácie do jedného objektu stavu. Zatiaľ máme v stave iba `account`, takže sa veľa nemení, ale vytvára to cestu pre ďalšie rozšírenia.
Musíme tiež aktualizovať funkcie, ktoré ho používajú. Vo funkciách `register()` a `login()` nahraďte `account = ...`s`state.account = ...`;
Musíme tiež aktualizovať funkcie, ktoré ho používajú. Vo funkciách `register()` a `login()` nahraďte `account = ...`za`state.account = ...`;
Na začiatok funkcie `updateDashboard()` pridajte tento riadok:
@ -85,15 +85,15 @@ Na začiatok funkcie `updateDashboard()` pridajte tento riadok:
const account = state.account;
```
Toto refaktorovanie samo o sebe neprinieslo veľké zlepšenia, ale myšlienkou bolo položiť základy pre ďalšie zmeny.
Táto refaktorizácia sama o sebe nepriniesla veľké zlepšenia, ale myšlienkou bolo položiť základy pre ďalšie zmeny.
## Sledovanie zmien dát
Teraz, keď sme zaviedli objekt `state` na ukladanie našich dát, ďalším krokom je centralizácia aktualizácií. Cieľom je uľahčiť sledovanie akýchkoľvek zmien a kedy sa dejú.
Teraz, keď sme zaviedli objekt `state` na ukladanie našich dát, ďalším krokom je centralizácia aktualizácií. Cieľom je uľahčiť sledovanie akýchkoľvek zmien a kedy k nim dochádza.
Aby sme zabránili zmenám objektu`state`, je tiež dobrým zvykom považovať ho za [*nemenný*](https://en.wikipedia.org/wiki/Immutable_object), čo znamená, že ho nemožno vôbec modifikovať. To tiež znamená, že musíte vytvoriť nový objekt stavu, ak chcete niečo v ňom zmeniť. Týmto spôsobom vytvárate ochranu pred potenciálne nežiaducimi [vedľajšími účinkami](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) a otvárate možnosti pre nové funkcie vo vašej aplikácii, ako je implementácia undo/redo, pričom tiež uľahčujete ladenie. Napríklad by ste mohli zaznamenávať každú zmenu vykonanú v stave a uchovávať históriu zmien, aby ste pochopili zdroj chyby.
Aby sme sa vyhli zmenám v objekte`state`, je tiež dobrým zvykom považovať ho za [*nemenný*](https://en.wikipedia.org/wiki/Immutable_object), čo znamená, že ho nemožno vôbec meniť. To tiež znamená, že musíte vytvoriť nový objekt stavu, ak chcete niečo v ňom zmeniť. Týmto spôsobom si vytvoríte ochranu pred potenciálne nežiaducimi [vedľajšími účinkami](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) a otvoríte možnosti pre nové funkcie vo vašej aplikácii, ako je implementácia funkcií späť/znova, pričom zároveň uľahčíte ladenie. Napríklad by ste mohli zaznamenávať každú zmenu vykonanú v stave a uchovávať históriu zmien, aby ste pochopili zdroj chyby.
V JavaScripte môžete použiť [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) na vytvorenie nemennej verzie objektu. Ak sa pokúsite vykonať zmeny na nemennom objekte, bude vyvolaná výnimka.
V JavaScripte môžete použiť [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) na vytvorenie nemennej verzie objektu. Ak sa pokúsite vykonať zmeny v nemennom objekte, vyvolá sa výnimka.
✅ Viete, aký je rozdiel medzi *povrchovo* a *hlboko* nemenným objektom? Môžete si o tom prečítať [tu](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
V tejto funkcii vytvárame nový objekt stavu a kopírujeme dáta z predchádzajúceho stavu pomocou [*spread operátora (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Potom prepíšeme konkrétnu vlastnosť objektu stavu novými dátami pomocou [notácie s hranatými zátvorkami](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]`na priradenie. Nakoniec uzamkneme objekt, aby sme zabránili modifikáciám pomocou `Object.freeze()`. Zatiaľ máme v stave uloženú iba vlastnosť `account`, ale s týmto prístupom môžete pridať toľko vlastností, koľko potrebujete.
V tejto funkcii vytvárame nový objekt stavu a kopírujeme dáta z predchádzajúceho stavu pomocou [*spread operátora (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Potom prepíšeme konkrétnu vlastnosť objektu stavu novými dátami pomocou [zátvorkovej notácie](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]`pre priradenie. Nakoniec objekt uzamkneme, aby sme zabránili jeho modifikáciám pomocou `Object.freeze()`. Zatiaľ máme v stave uloženú iba vlastnosť `account`, ale s týmto prístupom môžete do stavu pridať toľko vlastností, koľko potrebujete.
Aktualizujeme aj inicializáciu `state`, aby sme sa uistili, že počiatočný stav je tiež zmrazený:
Tiež aktualizujeme inicializáciu `state`, aby sme sa uistili, že počiatočný stav je tiež zmrazený:
```js
let state = Object.freeze({
@ -120,13 +120,13 @@ let state = Object.freeze({
});
```
Potom aktualizujte funkciu `register` nahradením priradenia `state.account = result;`s:
Potom aktualizujte funkciu `register` nahradením priradenia `state.account = result;`za:
```js
updateState('account', result);
```
Urobte to isté s funkciou `login`, nahraďte `state.account = data;`s:
Urobte to isté s funkciou `login`, nahraďte `state.account = data;`za:
```js
updateState('account', data);
@ -143,35 +143,35 @@ function logout() {
}
```
V `updateDashboard()` nahraďte presmerovanie `return navigate('/login');`s`return logout()`;
V `updateDashboard()` nahraďte presmerovanie `return navigate('/login');`za`return logout()`;
Skúste zaregistrovať nový účet, odhlásiť sa a znova prihlásiť, aby ste skontrolovali, či všetko stále funguje správne.
Skúste zaregistrovať nový účet, odhlásiť sa a znova prihlásiť, aby ste overili, že všetko stále funguje správne.
> Tip: môžete si pozrieť všetky zmeny stavu pridaním `console.log(state)` na koniec `updateState()` a otvorením konzoly vo vývojárskych nástrojoch vášho prehliadača.
> Tip: môžete si pozrieť všetky zmeny stavu pridaním `console.log(state)` na koniec `updateState()` a otvorením konzoly v nástrojoch pre vývojárov vášho prehliadača.
## Zachovanie stavu
Väčšina webových aplikácií potrebuje zachovať dáta, aby mohla správne fungovať. Všetky kritické dáta sú zvyčajne uložené v databáze a prístupné cez server API, ako napríklad údaje o používateľskom účte v našom prípade. Ale niekedy je tiež zaujímavé zachovať niektoré dáta na klientovi, ktorý beží vo vašom prehliadači, pre lepší používateľský zážitok alebo na zlepšenie výkonu načítania.
Väčšina webových aplikácií potrebuje zachovať dáta, aby mohla správne fungovať. Všetky kritické dáta sú zvyčajne uložené v databáze a prístupné cez server API, ako napríklad údaje o používateľskom účte v našom prípade. Niekedy je však zaujímavé zachovať niektoré dáta na strane klienta, ktorý beží vo vašom prehliadači, pre lepší používateľský zážitok alebo na zlepšenie výkonu načítania.
Keď chcete zachovať dáta vo vašom prehliadači, existuje niekoľko dôležitých otázok, ktoré by ste si mali položiť:
Keď chcete zachovať dáta vo vašom prehliadači, mali by ste si položiť niekoľko dôležitých otázok:
- *Sú dáta citlivé?* Mali by ste sa vyhnúť ukladaniu akýchkoľvek citlivých dát na klientovi, ako sú heslá používateľov.
- *Ako dlho potrebujete tieto dáta uchovávať?* Plánujete prístup k týmto dátam iba počas aktuálnej relácie alebo ich chcete uchovávať navždy?
- *Sú dáta citlivé?* Mali by ste sa vyhnúť ukladaniu akýchkoľvek citlivých dát na strane klienta, ako sú heslá používateľov.
- *Ako dlho potrebujete tieto dáta uchovávať?* Plánujete k týmto dátam pristupovať iba počas aktuálnej relácie alebo ich chcete uchovávať navždy?
Existuje viacero spôsobov ukladania informácií vo webovej aplikácii, v závislosti od toho, čo chcete dosiahnuť. Napríklad môžete použiť URL na uloženie vyhľadávacieho dotazu a sprístupniť ho medzi používateľmi. Môžete tiež použiť [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), ak je potrebné zdieľať dáta so serverom, ako napríklad informácie o [autentifikácii](https://en.wikipedia.org/wiki/Authentication).
Existuje niekoľko spôsobov, ako ukladať informácie vo webovej aplikácii, v závislosti od toho, čo chcete dosiahnuť. Napríklad môžete použiť URL na uloženie vyhľadávacieho dotazu a sprístupniť ho na zdieľanie medzi používateľmi. Môžete tiež použiť [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), ak je potrebné dáta zdieľať so serverom, ako napríklad informácie o [autentifikácii](https://en.wikipedia.org/wiki/Authentication).
Ďalšou možnosťou je použiť jednu z mnohých API prehliadača na ukladanie dát. Dve z nich sú obzvlášť zaujímavé:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value úložisko](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), ktoré umožňuje zachovať dáta špecifické pre aktuálnu webovú stránku medzi rôznymi reláciami. Dáta uložené v ňom nikdy nevypršia.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funguje rovnako ako `localStorage`, okrem toho, že dáta uložené v ňom sú vymazané, keď relácia skončí (keď sa prehliadač zatvorí).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value úložisko](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), ktoré umožňuje zachovať dáta špecifické pre aktuálnu webovú stránku medzi rôznymi reláciami. Uložené dáta nikdy nevypršia.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): funguje rovnako ako `localStorage`, okrem toho, že uložené dáta sa vymažú, keď relácia skončí (keď sa prehliadač zatvorí).
Všimnite si, že obe tieto API umožňujú ukladať iba [reťazce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ak chcete uložiť komplexné objekty, budete ich musieť serializovať do formátu [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) pomocou [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Všimnite si, že obe tieto API umožňujú ukladať iba [reťazce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ak chcete ukladať zložité objekty, budete ich musieť serializovať do formátu [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) pomocou [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Ak chcete vytvoriť webovú aplikáciu, ktorá nepracuje so serverom, je tiež možné vytvoriť databázu na klientovi pomocou [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Táto možnosť je vyhradená pre pokročilé prípady použitia alebo ak potrebujete uložiť významné množstvo dát, pretože je zložitejšia na použitie.
✅ Ak chcete vytvoriť webovú aplikáciu, ktorá nepracuje so serverom, je tiež možné vytvoriť databázu na strane klienta pomocou [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Táto možnosť je vyhradená pre pokročilé prípady použitia alebo ak potrebujete ukladať významné množstvo dát, pretože je zložitejšia na použitie.
### Úloha
Chceme, aby naši používatelia zostali prihlásení, kým explicitne nekliknú na tlačidlo *Odhlásiť sa*, takže použijeme `localStorage` na uloženie údajov o účte. Najprv definujme kľúč, ktorý použijeme na uloženie našich dát.
Chceme, aby naši používatelia zostali prihlásení, kým výslovne nekliknú na tlačidlo *Odhlásiť sa*, takže použijeme `localStorage` na uloženie údajov o účte. Najprv definujme kľúč, ktorý budeme používať na uloženie našich dát.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Potom pridajte tento riadok na koniec funkcie `updateState()`:
Týmto spôsobom budú údaje o používateľskom účte zachované a vždy aktuálne, ako sme predtým centralizovali všetky aktualizácie stavu. Tu začíname využívať všetky naše predchádzajúce refaktory 🙂.
Týmto spôsobom budú údaje o používateľskom účte zachované a vždy aktuálne, pretože sme predtým centralizovali všetky aktualizácie stavu. Tu začíname ťažiť zo všetkých našich predchádzajúcich refaktorov 🙂.
Keďže dáta sú uložené, musíme sa tiež postarať o ich obnovenie, keď sa aplikácia načíta. Keďže začneme mať viac inicializačného kódu, môže byť dobrý nápad vytvoriť novú funkciu `init`, ktorá tiež zahŕňa náš predchádzajúci kód na konci `app.js`:
Keďže sú dáta uložené, musíme sa tiež postarať o ich obnovenie pri načítaní aplikácie. Keďže začneme mať viac inicializačného kódu, môže byť dobrý nápad vytvoriť novú funkciu `init`, ktorá zahŕňa aj náš predchádzajúci kód na konci `app.js`:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Tu získavame uložené dáta, a ak nejaké existujú, aktualizujeme stav podľa nich. Je dôležité to urobiť *pred* aktualizáciou trasy, pretože môže existovať kód, ktorý sa spolieha na stav počas aktualizácie stránky.
Tu načítame uložené dáta a ak nejaké existujú, aktualizujeme stav podľa nich. Je dôležité to urobiť *pred* aktualizáciou trasy, pretože môže existovať kód, ktorý sa spolieha na stav počas aktualizácie stránky.
Môžeme tiež urobiť stránku *Dashboard* predvolenou stránkou našej aplikácie, pretože teraz zachovávame údaje o účte. Ak sa nenájdu žiadne dáta, dashboard sa postará o presmerovanie na stránku *Login*. V `updateRoute()` nahraďte fallback `return navigate('/login');` s`return navigate('/dashboard');`.
Môžeme tiež nastaviť stránku *Dashboard* ako predvolenú stránku našej aplikácie, pretože teraz zachovávame údaje o účte. Ak sa nenájdu žiadne dáta, hlavný panel sa postará o presmerovanie na stránku *Login*. V `updateRoute()` nahraďte záložné `return navigate('/login');` za`return navigate('/dashboard');`.
Teraz sa prihláste do aplikácie a skúste obnoviť stránku. Mali by ste zostať na dashboarde. S touto aktualizáciou sme sa postarali o všetky naše počiatočné problémy...
Teraz sa prihláste do aplikácie a skúste obnoviť stránku. Mali by ste zostať na hlavnom paneli. S touto aktualizáciou sme vyriešili všetky naše počiatočné problémy...
## Obnovenie dát
...Ale mohli sme tiež vytvoriť nový problém. Ups!
Prejdite na dashboard pomocou účtu `test`, potom spustite tento príkaz v termináli na vytvorenie novej transakcie:
Prejdite na hlavný panel pomocou účtu `test`, potom spustite tento príkaz v termináli na vytvorenie novej transakcie:
Skúste teraz obnoviť stránku dashboardu v prehliadači. Čo sa stane? Vidíte novú transakciu?
Skúste teraz obnoviť stránku hlavného panela vo vašom prehliadači. Čo sa stane? Vidíte novú transakciu?
Stav je zachovaný na neurčito vďaka `localStorage`, ale to tiež znamená, že sa nikdy neaktualizuje, kým sa neodhlásite z aplikácie a znova neprihlásite!
Stav je zachovaný na neurčito vďaka `localStorage`, ale to tiež znamená, že sa nikdy neaktualizuje, kým sa z aplikácie neodhlásite a znova neprihlásite!
Jednou možnou stratégiou na opravu je načítať údaje o účte zakaždým, keď sa dashboard načíta, aby sa zabránilo zastaraným dátam.
Jednou z možných stratégií na opravu tohto problému je načítať údaje o účte zakaždým, keď sa načíta hlavný panel, aby sa predišlo zastaraným dátam.
### Úloha
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Táto metóda kontroluje, či sme aktuálne prihlásení, a potom načíta údaje o účte zo servera.
Táto metóda overí, že sme aktuálne prihlásení, a potom načíta údaje o účte zo servera.
Vytvorte ďalšiu funkciu s názvom `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Táto funkcia aktualizuje údaje o účte a potom sa postará o aktualizáciu HTML stránky dashboardu. Je to to, čo potrebujeme zavolať, keď sa načíta trasa dashboardu. Aktualizujte definíciu trasy s:
Táto funkcia aktualizuje údaje o účte a potom sa postará o aktualizáciu HTML hlavného panela. To je to, čo potrebujeme zavolať, keď sa načíta trasa hlavného panela. Aktualizujte definíciu trasy:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
Skúste teraz obnoviť dashboard, mal by zobrazovať aktualizované údaje o účte.
Skúste teraz obnoviť hlavný panel, mal by zobrazovať aktualizované údaje o účte.
---
## 🚀 Výzva
Teraz, keď načítavame údaje o účte zakaždým, keď sa načíta dashboard, myslíte si, že stále potrebujeme zachovať *všetky údaje o účte*?
Teraz, keď načítavame údaje o účte zakaždým, keď sa načíta hlavný panel, myslíte si, že stále potrebujeme zachovať *všetky údaje o účte*?
Skúste spolupracovať na zmene toho, čo sa ukladá a načítava z `localStorage`, aby obsahovalo iba to, čo je absolútne potrebné na fungovanie aplikácie.
Skúste spolupracovať na zmene toho, čo sa ukladá a načítava z `localStorage`, aby obsahovalo iba to, čo je absolútne nevyhnutné pre fungovanie aplikácie.
## Kvíz po prednáške
[Kvíz po prednáške](https://ff-quizzes.netlify.app/web/quiz/48)
Tento dokument bol preložený pomocou služby na automatický preklad [Co-op Translator](https://github.com/Azure/co-op-translator). Aj keď sa snažíme o presnosť, upozorňujeme, že automatické preklady môžu obsahovať chyby alebo nepresnosti. Pôvodný dokument v jeho pôvodnom jazyku by mal byť považovaný za autoritatívny zdroj. Pre kritické informácie sa odporúča profesionálny ľudský preklad. Nezodpovedáme za akékoľvek nedorozumenia alebo nesprávne interpretácie vyplývajúce z použitia tohto prekladu.
Tento dokument bol preložený pomocou služby na automatický preklad [Co-op Translator](https://github.com/Azure/co-op-translator). Hoci sa snažíme o presnosť, upozorňujeme, že automatické preklady môžu obsahovať chyby alebo nepresnosti. Pôvodný dokument v jeho pôvodnom jazyku by mal byť považovaný za autoritatívny zdroj. Pre dôležité informácie odporúčame profesionálny ľudský preklad. Nezodpovedáme za žiadne nedorozumenia alebo nesprávne interpretácie vyplývajúce z použitia tohto prekladu.
# Izdelava bančne aplikacije, 4. del: Koncepti upravljanja stanja
# Ustvarjanje bančne aplikacije, 4. del: Koncepti upravljanja stanja
## Predhodni kviz
@ -15,13 +15,13 @@ CO_OP_TRANSLATOR_METADATA:
### Uvod
Ko spletna aplikacija raste, postane težko slediti vsem tokovom podatkov. Kateri del kode pridobi podatke, katera stran jih uporablja, kje in kdaj jih je treba posodobiti... hitro se lahko znajdemo v zmešnjavi kode, ki jo je težko vzdrževati. To je še posebej res, ko je treba podatke deliti med različnimi stranmi aplikacije, na primer podatke o uporabniku. Koncept *upravljanja stanja* je vedno obstajal v vseh vrstah programov, vendar je zaradi naraščajoče kompleksnosti spletnih aplikacij postal ključna točka pri razvoju.
Ko spletna aplikacija raste, postane težko slediti vsem tokovom podatkov. Kateri del kode pridobi podatke, katera stran jih uporablja, kje in kdaj jih je treba posodobiti... hitro se lahko znajdemo z zmedeno kodo, ki jo je težko vzdrževati. To je še posebej res, ko je treba podatke deliti med različnimi stranmi aplikacije, na primer podatke o uporabniku. Koncept *upravljanja stanja* je vedno obstajal v vseh vrstah programov, vendar je zaradi naraščajoče kompleksnosti spletnih aplikacij zdaj ključna točka, o kateri je treba razmišljati med razvojem.
V tem zadnjem delu bomo pregledali aplikacijo, ki smo jo zgradili, da ponovno premislimo, kako je stanje upravljano, kar bo omogočilo podporo osvežitvi brskalnika kadarkoli in ohranjanje podatkov med uporabniškimi sejami.
V tem zadnjem delu bomo pregledali aplikacijo, ki smo jo zgradili, da ponovno premislimo, kako je stanje upravljano, kar omogoča podporo osvežitvi brskalnika kadarkoli in ohranjanje podatkov med uporabniškimi sejami.
### Predpogoji
Za to lekcijo morate dokončati [pridobivanje podatkov](../3-data/README.md) v spletni aplikaciji. Prav tako morate namestiti [Node.js](https://nodejs.org) in [zagnati strežniški API](../api/README.md) lokalno, da lahko upravljate podatke o računih.
Za to lekcijo morate dokončati [pridobivanje podatkov](../3-data/README.md) v aplikaciji. Prav tako morate namestiti [Node.js](https://nodejs.org) in [zagnati strežniški API](../api/README.md) lokalno, da lahko upravljate podatke o računih.
Preverite, ali strežnik pravilno deluje, tako da v terminalu zaženete naslednji ukaz:
@ -34,28 +34,28 @@ curl http://localhost:5000/api
## Ponovno premislimo upravljanje stanja
V [prejšnji lekciji](../3-data/README.md) smo v aplikaciji predstavili osnovni koncept stanja z globalno spremenljivko `account`, ki vsebuje bančne podatke trenutno prijavljenega uporabnika. Vendar ima naša trenutna implementacija nekaj pomanjkljivosti. Poskusite osvežiti stran, ko ste na nadzorni plošči. Kaj se zgodi?
V [prejšnji lekciji](../3-data/README.md) smo predstavili osnovni koncept stanja v naši aplikaciji z globalno spremenljivko `account`, ki vsebuje bančne podatke trenutno prijavljenega uporabnika. Vendar ima naša trenutna implementacija nekaj pomanjkljivosti. Poskusite osvežiti stran, ko ste na nadzorni plošči. Kaj se zgodi?
Trenutna koda ima tri težave:
- Stanje ni ohranjeno, saj vas osvežitev brskalnika vrne na stran za prijavo.
- Obstaja več funkcij, ki spreminjajo stanje. Ko aplikacija raste, postane težko slediti spremembam in zlahka pozabimo posodobiti eno od njih.
- Obstaja več funkcij, ki spreminjajo stanje. Ko aplikacija raste, to lahko oteži sledenje spremembam in zlahka pozabimo posodobiti eno od njih.
- Stanje ni očiščeno, zato so podatki o računu še vedno prisotni, ko kliknete *Odjava*, čeprav ste na strani za prijavo.
Te težave bi lahko reševali eno za drugo, vendar bi to ustvarilo več podvajanja kode in aplikacijo naredilo bolj zapleteno ter težje vzdrževano. Ali pa bi si lahko vzeli nekaj minut in premislili našo strategijo.
Lahko bi posodobili našo kodo, da bi te težave reševali eno za drugo, vendar bi to ustvarilo več podvajanja kode in naredilo aplikacijo bolj zapleteno ter težje vzdrževano. Ali pa bi si lahko vzeli nekaj minut in premislili našo strategijo.
> Katere težave pravzaprav poskušamo rešiti?
> Katere težave pravzaprav poskušamo rešiti tukaj?
[Upravljanje stanja](https://en.wikipedia.org/wiki/State_management) se osredotoča na iskanje dobrega pristopa za reševanje teh dveh specifičnih težav:
- Kako ohraniti tokove podatkov v aplikaciji razumljive?
- Kako zagotoviti, da so podatki stanja vedno usklajeni z uporabniškim vmesnikom (in obratno)?
- Kako ohraniti podatke o stanju vedno usklajene z uporabniškim vmesnikom (in obratno)?
Ko se lotite teh težav, so lahko druge težave že rešene ali pa jih je lažje odpraviti. Obstaja veliko možnih pristopov za reševanje teh težav, vendar bomo uporabili običajno rešitev, ki vključuje **centralizacijo podatkov in načinov za njihovo spreminjanje**. Tokovi podatkov bi potekali takole:
Ko se lotite teh težav, so lahko vse druge težave, ki jih imate, že rešene ali pa jih je lažje rešiti. Obstaja veliko možnih pristopov za reševanje teh težav, vendar bomo uporabili običajno rešitev, ki vključuje **centralizacijo podatkov in načinov za njihovo spreminjanje**. Tokovi podatkov bi potekali takole:

> Tukaj ne bomo obravnavali dela, kjer podatki samodejno sprožijo posodobitev pogleda, saj je povezan z naprednejšimi koncepti [reaktivnega programiranja](https://en.wikipedia.org/wiki/Reactive_programming). To je dobra tema za nadaljnje poglobljeno raziskovanje.
> Tukaj ne bomo obravnavali dela, kjer podatki samodejno sprožijo posodobitev pogleda, saj je to povezano z naprednejšimi koncepti [reaktivnega programiranja](https://en.wikipedia.org/wiki/Reactive_programming). To je dobra tema za nadaljnje poglobljeno raziskovanje.
✅ Obstaja veliko knjižnic z različnimi pristopi k upravljanju stanja, [Redux](https://redux.js.org) pa je priljubljena izbira. Oglejte si koncepte in vzorce, ki jih uporablja, saj je to pogosto dober način za učenje o potencialnih težavah, s katerimi se lahko soočate v velikih spletnih aplikacijah, in kako jih je mogoče rešiti.
@ -75,9 +75,9 @@ let state = {
};
```
Ideja je, da *centraliziramo* vse podatke aplikacije v enem samem objektu stanja. Trenutno imamo v stanju samo `account`, zato se ne spremeni veliko, vendar to odpira pot za nadaljnje izboljšave.
Ideja je, da *centraliziramo* vse podatke naše aplikacije v enem samem objektu stanja. Trenutno imamo v stanju samo `account`, zato se ne spremeni veliko, vendar ustvarja pot za prihodnje nadgradnje.
Posodobiti moramo tudi funkcije, ki ga uporabljajo. V funkcijah `register()` in `login()` zamenjajte `account = ...` z `state.account = ...`;
Prav tako moramo posodobiti funkcije, ki ga uporabljajo. V funkcijah `register()` in `login()` zamenjajte `account = ...` z `state.account = ...`;
Na vrhu funkcije `updateDashboard()` dodajte to vrstico:
@ -91,11 +91,11 @@ To prestrukturiranje samo po sebi ni prineslo veliko izboljšav, vendar je ideja
Zdaj, ko smo vzpostavili objekt `state` za shranjevanje naših podatkov, je naslednji korak centralizacija posodobitev. Cilj je olajšati sledenje vsem spremembam in kdaj se zgodijo.
Da bi se izognili spremembam objekta `state`, je dobra praksa, da ga obravnavamo kot [*nepremičnega*](https://en.wikipedia.org/wiki/Immutable_object), kar pomeni, da ga ni mogoče spreminjati. To pomeni, da morate ustvariti nov objekt stanja, če želite kaj spremeniti v njem. S tem se zaščitite pred morebitnimi neželenimi [stranskimi učinki](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) in odprete možnosti za nove funkcije v aplikaciji, kot je implementacija razveljavitve/ponovne uveljavitve, hkrati pa olajšate odpravljanje napak. Na primer, lahko beležite vsako spremembo stanja in hranite zgodovino sprememb, da razumete vir napake.
Da bi se izognili spremembam objekta `state`, je dobra praksa, da ga obravnavamo kot [*nepremičnega*](https://en.wikipedia.org/wiki/Immutable_object), kar pomeni, da ga sploh ni mogoče spreminjati. To pomeni, da morate ustvariti nov objekt stanja, če želite karkoli spremeniti v njem. S tem se zaščitite pred morebitnimi neželenimi [stranskimi učinki](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) in odprete možnosti za nove funkcije v vaši aplikaciji, kot je implementacija razveljavitve/ponovne uveljavitve, hkrati pa olajšate odpravljanje napak. Na primer, lahko beležite vsako spremembo stanja in hranite zgodovino sprememb, da razumete vir napake.
V JavaScriptu lahko uporabite [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) za ustvarjanje nepremičnega objekta. Če poskušate narediti spremembe na nepremičnem objektu, bo sprožena izjema.
V JavaScriptu lahko uporabite [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) za ustvarjanje nepremičnega objekta. Če poskušate narediti spremembe nepremičnemu objektu, bo sprožena izjema.
✅ Ali poznate razliko med *plitkim* in *globokim* nepremičnim objektom? Preberite več o tem [tukaj](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Ali poznate razliko med *plitkim* in *globokim* nepremičnim objektom? Preberite o tem [tukaj](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Naloga
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
V tej funkciji ustvarimo nov objekt stanja in kopiramo podatke iz prejšnjega stanja z uporabo [*operatorja razširitve (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Nato preglasimo določeno lastnost objekta stanja z novimi podatki z uporabo [notacije z oglatimi oklepaji](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` za dodelitev. Na koncu zaklenemo objekt, da preprečimo spremembe z uporabo`Object.freeze()`. Trenutno imamo v stanju shranjeno samo lastnost `account`, vendar lahko s tem pristopom dodate toliko lastnosti, kot jih potrebujete.
V tej funkciji ustvarjamo nov objekt stanja in kopiramo podatke iz prejšnjega stanja z uporabo [*operatorja razširitve (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Nato preglasimo določeno lastnost objekta stanja z novimi podatki z uporabo [notacije z oglatimi oklepaji](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` za dodelitev. Na koncu zaklenemo objekt, da preprečimo spremembe z `Object.freeze()`. Trenutno imamo v stanju shranjeno samo lastnost `account`, vendar lahko s tem pristopom dodate toliko lastnosti, kot jih potrebujete.
Posodobili bomo tudi inicializacijo `state`, da zagotovimo, da je začetno stanje prav tako zamrznjeno:
Prav tako bomo posodobili inicializacijo `state`, da zagotovimo, da je začetno stanje tudi zamrznjeno:
```js
let state = Object.freeze({
@ -145,20 +145,20 @@ function logout() {
V `updateDashboard()` zamenjajte preusmeritev `return navigate('/login');` z `return logout();`;
Poskusite registrirati nov račun, se odjaviti in ponovno prijaviti, da preverite, ali vse še vedno deluje pravilno.
Poskusite registrirati nov račun, se odjaviti in se ponovno prijaviti, da preverite, ali vse še vedno deluje pravilno.
> Nasvet: lahko si ogledate vse spremembe stanja tako, da dodate `console.log(state)` na dnu funkcije `updateState()` in odprete konzolo v orodjih za razvijalce vašega brskalnika.
> Nasvet: lahko si ogledate vse spremembe stanja tako, da dodate `console.log(state)` na dnu `updateState()` in odprete konzolo v orodjih za razvijalce vašega brskalnika.
## Ohranjanje stanja
Večina spletnih aplikacij mora ohranjati podatke, da lahko pravilno deluje. Vsi ključni podatki so običajno shranjeni v podatkovni bazi in dostopni prek strežniškega API-ja, kot so podatki o uporabniških računih v našem primeru. Včasih pa je zanimivo ohraniti nekatere podatke v odjemalski aplikaciji, ki se izvaja v vašem brskalniku, za boljšo uporabniško izkušnjo ali izboljšanje zmogljivosti nalaganja.
Večina spletnih aplikacij mora ohraniti podatke, da lahko pravilno deluje. Vsi ključni podatki so običajno shranjeni v bazi podatkov in dostopni prek strežniškega API-ja, kot so podatki o uporabniških računih v našem primeru. Včasih pa je zanimivo ohraniti nekatere podatke v odjemalski aplikaciji, ki se izvaja v vašem brskalniku, za boljšo uporabniško izkušnjo ali za izboljšanje zmogljivosti nalaganja.
Ko želite ohraniti podatke v brskalniku, si morate zastaviti nekaj pomembnih vprašanj:
- *Ali so podatki občutljivi?* Izogibajte se shranjevanju občutljivih podatkov na odjemalcu, kot so gesla uporabnikov.
- *Kako dolgo potrebujete te podatke?* Ali nameravate dostopati do teh podatkov samo med trenutno sejo ali jih želite shraniti za vedno?
Obstaja več načinov za shranjevanje informacij znotraj spletne aplikacije, odvisno od tega, kaj želite doseči. Na primer, lahko uporabite URL-je za shranjevanje iskalne poizvedbe in jo naredite deljivo med uporabniki. Lahko uporabite tudi [HTTP piškotke](https://developer.mozilla.org/docs/Web/HTTP/Cookies), če je treba podatke deliti s strežnikom, na primer informacije o [avtentikaciji](https://en.wikipedia.org/wiki/Authentication).
Obstaja več načinov za shranjevanje informacij znotraj spletne aplikacije, odvisno od tega, kaj želite doseči. Na primer, lahko uporabite URL-je za shranjevanje iskalne poizvedbe in jo naredite deljivo med uporabniki. Prav tako lahko uporabite [HTTP piškotke](https://developer.mozilla.org/docs/Web/HTTP/Cookies), če je treba podatke deliti s strežnikom, kot so informacije o [avtentikaciji](https://en.wikipedia.org/wiki/Authentication).
Druga možnost je uporaba ene od številnih API-jev brskalnika za shranjevanje podatkov. Dva od njih sta še posebej zanimiva:
@ -167,11 +167,11 @@ Druga možnost je uporaba ene od številnih API-jev brskalnika za shranjevanje p
Upoštevajte, da oba API-ja omogočata shranjevanje samo [nizov](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Če želite shraniti kompleksne objekte, jih boste morali serializirati v format [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) z uporabo [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Če želite ustvariti spletno aplikacijo, ki ne deluje s strežnikom, je mogoče ustvariti podatkovno bazo na odjemalcu z uporabo API-ja [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ta je rezerviran za napredne primere uporabe ali če morate shraniti večjo količino podatkov, saj je bolj zapleten za uporabo.
✅ Če želite ustvariti spletno aplikacijo, ki ne deluje s strežnikom, je mogoče ustvariti bazo podatkov na odjemalcu z uporabo API-ja [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ta je rezerviran za napredne primere uporabe ali če morate shraniti večjo količino podatkov, saj je bolj zapleten za uporabo.
### Naloga
Želimo, da naši uporabniki ostanejo prijavljeni, dokler izrecno ne kliknejo gumba*Odjava*, zato bomo uporabili `localStorage` za shranjevanje podatkov o računu. Najprej definirajmo ključ, ki ga bomo uporabili za shranjevanje naših podatkov.
Želimo, da naši uporabniki ostanejo prijavljeni, dokler izrecno ne kliknejo na gumb *Odjava*, zato bomo uporabili `localStorage` za shranjevanje podatkov o računu. Najprej definirajmo ključ, ki ga bomo uporabili za shranjevanje naših podatkov.
S tem bodo podatki o uporabniškem računu ohranjeni in vedno posodobljeni, saj smo prej centralizirali vse posodobitve stanja. Tukaj začnemo izkoriščati vse naše prejšnje prestrukturiranje 🙂.
Ker so podatki shranjeni, moramo poskrbeti tudi za njihovo obnovitev, ko se aplikacija naloži. Ker bomo začeli imeti več inicializacijske kode, je morda dobra ideja, da ustvarimo novo funkcijo `init`, ki vključuje tudi našo prejšnjo kodo na dnu `app.js`:
Ker so podatki shranjeni, moramo poskrbeti tudi za njihovo obnovitev, ko se aplikacija naloži. Ker bomo začeli imeti več inicializacijske kode, je morda dobra ideja ustvariti novo funkcijo `init`, ki vključuje tudi našo prejšnjo kodo na dnu `app.js`:
```js
function init() {
@ -204,15 +204,15 @@ init();
Tukaj pridobimo shranjene podatke, in če obstajajo, ustrezno posodobimo stanje. Pomembno je, da to storimo *pred* posodobitvijo poti, saj lahko obstaja koda, ki se zanaša na stanje med posodobitvijo strani.
Privzeto stran naše aplikacije lahko naredimo *Nadzorno ploščo*, saj zdaj ohranjamo podatke o računu. Če podatki niso najdeni, nadzorna plošča poskrbi za preusmeritev na stran *Prijava*. V `updateRoute()` zamenjajte privzeto `return navigate('/login');` z `return navigate('/dashboard');`.
Prav tako lahko naredimo stran *Nadzorna plošča* privzeto stran naše aplikacije, saj zdaj ohranjamo podatke o računu. Če podatki niso najdeni, nadzorna plošča poskrbi za preusmeritev na stran *Prijava*. V `updateRoute()` zamenjajte privzeto `return navigate('/login');` z `return navigate('/dashboard');`.
Zdaj se prijavite v aplikacijo in poskusite osvežiti stran. Ostati bi morali na nadzorni plošči. S to posodobitvijo smo odpravili vse naše začetne težave...
Zdaj se prijavite v aplikacijo in poskusite osvežiti stran. Ostati bi morali na nadzorni plošči. S to posodobitvijo smo rešili vse naše začetne težave...
## Osvežitev podatkov
...Ampak morda smo ustvarili novo težavo. Ups!
Pojdite na nadzorno ploščo z računom `test`, nato zaženite ta ukaz v terminalu za ustvarjanje nove transakcije:
Pojdite na nadzorno ploščo z računom `test`, nato zaženite ta ukaz v terminalu, da ustvarite novo transakcijo:
Zdaj poskusite osvežiti stran nadzorne plošče v brskalniku. Kaj se zgodi? Ali vidite novo transakcijo?
Poskusite osvežiti stran nadzorne plošče v brskalniku zdaj. Kaj se zgodi? Ali vidite novo transakcijo?
Stanje je ohranjeno za nedoločen čas zahvaljujoč `localStorage`, vendar to pomeni, da se nikoli ne posodobi, dokler se ne odjavite iz aplikacije in ponovno prijavite!
Stanje je ohranjeno za nedoločen čas zahvaljujoč `localStorage`, vendar to pomeni, da se nikoli ne posodobi, dokler se ne odjavite iz aplikacije in se ponovno prijavite!
Ena od možnih strategij za odpravo te težave je, da vsakič, ko se naloži nadzorna plošča, ponovno naložimo podatke o računu, da se izognemo zastarelim podatkom.
Ena možna strategija za odpravo tega je, da ponovno naložimo podatke o računu vsakič, ko se naloži nadzorna plošča, da se izognemo zastarelim podatkom.
### Naloga
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Ta metoda preveri, ali smo trenutno prijavljeni, nato pa ponovno naloži podatke o računu s strežnika.
Ta metoda preveri, ali smo trenutno prijavljeni, nato ponovno naloži podatke o računu s strežnika.
Ustvarite drugo funkcijo z imenom `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Ta funkcija posodobi podatke o računu, nato pa poskrbi za posodobitev HTML-ja na strani nadzorne plošče. To je tisto, kar moramo poklicati, ko se naloži pot nadzorne plošče. Posodobite definicijo poti z:
Ta funkcija posodobi podatke o računu, nato poskrbi za posodobitev HTML-ja na strani nadzorne plošče. To je tisto, kar moramo poklicati, ko se naloži pot nadzorne plošče. Posodobite definicijo poti z:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
Zdaj poskusite osvežiti nadzorno ploščo, prikazati bi morali posodobljene podatke o računu.
Poskusite ponovno naložiti nadzorno ploščo zdaj, prikazati bi morali posodobljene podatke o računu.
---
## 🚀 Izziv
Zdaj, ko vsakič, ko se naloži nadzorna plošča, ponovno naložimo podatke o računu, ali menite, da moramo še vedno ohranjati *vse podatke o računu*?
Zdaj, ko ponovno nalagamo podatke o računu vsakič, ko se naloži nadzorna plošča, ali menite, da še vedno potrebujemo ohranjanje *vseh podatkov o računu*?
Poskusite skupaj spremeniti, kaj je shranjeno in naloženo iz `localStorage`, da vključuje samo tisto, kar je absolutno potrebno za delovanje aplikacije.


---
**Omejitev odgovornosti**:
Ta dokument je bil preveden z uporabo storitve za prevajanje z umetno inteligenco [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem maternem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.
Ta dokument je bil preveden z uporabo storitve AI za prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem maternem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.
# Изградња банкарске апликације, део 4: Концепти управљања стањем
# Изградња апликације за банкарство, део 4: Концепти управљања стањем
## Квиз пре предавања
@ -15,13 +15,13 @@ CO_OP_TRANSLATOR_METADATA:
### Увод
Како веб апликација расте, постаје изазов пратити све токове података. Који код добија податке, која страница их користи, где и када треба да се ажурирају... лако је завршити са неуредним кодом који је тешко одржавати. Ово је посебно тачно када треба да делите податке између различитих страница ваше апликације, на пример корисничке податке. Концепт *управљања стањем*одувек је постојао у свим врстама програма, али како веб апликације постају све сложеније, сада је то кључна тачка о којој треба размишљати током развоја.
Како веб апликација расте, постаје изазовно пратити све токове података. Који код добија податке, која страница их користи, где и када треба да се ажурирају... лако је завршити са неуредним кодом који је тешко одржавати. Ово је посебно тачно када треба да делите податке између различитих страница апликације, на пример корисничке податке. Концепт *управљања стањем* увек је постојао у свим врстама програма, али како веб апликације постају све сложеније, сада је то кључна тачка о којој треба размишљати током развоја.
У овом завршном делу, прегледаћемо апликацију коју смо изградили како бисмо преиспитали како се стање управља, омогућавајући подршку за освежавање прегледача у било ком тренутку и чување података током корисничких сесија.
У овом завршном делу, размотрићемо апликацију коју смо изградили како бисмо преиспитали начин управљања стањем, омогућавајући подршку за освежавање прегледача у било ком тренутку и чување података током корисничких сесија.
### Предуслови
Потребно је да сте завршили [добијање података](../3-data/README.md) део веб апликације за ову лекцију. Такође треба да инсталирате [Node.js](https://nodejs.org) и [покренете сервер API](../api/README.md) локално како бисте могли да управљате подацима о налогу.
Потребно је да сте завршили [преузимање података](../3-data/README.md) у претходном делу веб апликације за ову лекцију. Такође треба да инсталирате [Node.js](https://nodejs.org) и [покренете сервер API](../api/README.md) локално како бисте могли да управљате подацима о налогу.
Можете тестирати да ли сервер ради исправно извршавањем ове команде у терминалу:
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## Преиспитивање управљања стањем
У [претходној лекцији](../3-data/README.md), увели смо основни концепт стања у нашој апликацији са глобалном променљивом `account` која садржи банкарске податке за тренутно пријављеног корисника. Међутим, наша тренутна имплементација има неке недостатке. Покушајте да освежите страницу када сте на контролној табли. Шта се дешава?
У [претходној лекцији](../3-data/README.md), увели смо основни концепт стања у нашој апликацији са глобалном променљивом `account` која садржи банкарске податке за тренутно пријављеног корисника. Међутим, наша тренутна имплементација има неке недостатке. Покушајте да освежите страницу док сте на контролној табли. Шта се дешава?
Постоје три проблема са тренутним кодом:
- Стање није сачувано, јер освежавање прегледача враћа вас на страницу за пријаву.
- Постоји више функција које мењају стање. Како апликација расте, то може отежати праћење промена и лако је заборавити да се нешто ажурира.
- Стање се не чисти, тако да када кликнете на *Одјава*, подаци о налогу и даље постоје иако сте на страници за пријаву.
- Постоји више функција које мењају стање. Како апликација расте, то може отежати праћење промена и лако је заборавити да ажурирате једну.
- Стање се не чисти, па када кликнете на *Одјави се*, подаци о налогу и даље остају, иако сте на страници за пријаву.
Могли бисмо ажурирати наш код како бисмо решили ове проблеме један по један, али то би створило више дуплирања кода и учинило апликацију сложенијом и тежом за одржавање. Или бисмо могли да застанемо на неколико минута и преиспитамо нашу стратегију.
Могли бисмо ажурирати наш код да решимо ове проблеме један по један, али то би створило више дуплирања кода и учинило апликацију сложенијом и тежом за одржавање. Или бисмо могли да паузирамо на неколико минута и преиспитамо нашу стратегију.
> Које проблеме заправо покушавамо да решимо овде?
> Које проблеме заиста покушавамо да решимо овде?
[Управљање стањем](https://en.wikipedia.org/wiki/State_management) се односи на проналажење доброг приступа за решавање ова два конкретна проблема:
- Како одржати токове података у апликацији разумљивим?
- Како учинити токове података у апликацији разумљивим?
- Како одржати податке о стању увек синхронизованим са корисничким интерфејсом (и обрнуто)?
Када се позабавите овим питањима, сви други проблеми које можда имате или ће већ бити решени или ће постати лакши за решавање. Постоји много могућих приступа за решавање ових проблема, али ми ћемо се определити за уобичајено решење које се састоји од **централизовања података и начина њиховог мењања**. Токови података би изгледали овако:
Када се позабавите овим питањима, било који други проблеми које можда имате могу бити већ решени или постати лакши за решавање. Постоји много могућих приступа за решавање ових проблема, али ми ћемо се определити за уобичајено решење које се састоји од **централизовања података и начина њиховог мењања**. Токови података би изгледали овако:

> Овде нећемо покривати део где подаци аутоматски покрећу ажурирање приказа, јерје то повезано са напреднијим концептима [реактивног програмирања](https://en.wikipedia.org/wiki/Reactive_programming). Тоје добра тема за дубље истраживање ако сте заинтересовани.
✅ Постоји много библиотека са различитим приступима управљању стањем, [Redux](https://redux.js.org) је популарна опција. Погледајте концепте и обрасце који се користе јерје то често добар начин да научите са којим потенцијалним проблемима се можете суочити у великим веб апликацијама и како их можете решити.
✅ Постоји много библиотека са различитим приступима управљању стањем, [Redux](https://redux.js.org) је популарна опција. Погледајте концепте и обрасце који се користе,јерје то често добар начин да научите о потенцијалним проблемима са којима се можете суочити у великим веб апликацијама и како их можете решити.
### Задатак
@ -75,7 +75,7 @@ let state = {
};
```
Идеја је да се*централизују* сви подаци апликације у један објекат стања. Тренутно имамо само `account`у стању, тако да се много тога не мења, али ово ствара пут за будуће еволуције.
Идеја је да *централизујемо* све податке наше апликације у један објекат стања. Тренутно имамо само `account`у стању, тако да се много не мења, али то ствара пут за будуће еволуције.
Такође морамо ажурирати функције које га користе. У функцијама `register()` и `login()`, замените `account = ...`са`state.account = ...`;
@ -85,17 +85,17 @@ let state = {
const account = state.account;
```
Ово рефакторисање само по себи није донело много побољшања, али идеја је била да се поставе темељи за следеће промене.
Ово рефакторисање само по себи није донело много побољшања, али идеја је била да се постави основа за следеће промене.
## Праћење промена података
Сада када смо поставили објекат `state` за чување наших података, следећи корак је централизовање ажурирања. Циљ је да се олакша праћење било каквих промена и када се оне дешавају.
Да бисмо избегли промене направљене у објекту `state`, добра је пракса да га сматрамо [*непроменљивим*](https://en.wikipedia.org/wiki/Immutable_object), што значи да се уопште не може мењати. Ово такође значи да морате креирати нови објекат стања ако желите да промените било шта у њему. На овај начин градите заштиту од потенцијално нежељених [нуспојава](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) и отварате могућности за нове функције у вашој апликацији, као што је имплементација поништавања/поновног извршавања, док такође олакшавате отклањање грешака. На пример, могли бисте бележити сваку промену направљену у стању и чувати историју промена како бисте разумели извор грешке.
Да бисмо избегли промене направљене на објекту `state`, такође је добра пракса да га сматрамо [*непроменљивим*](https://en.wikipedia.org/wiki/Immutable_object), што значи да се уопште не може мењати. То такође значи да морате креирати нови објекат стања ако желите да промените било шта у њему. На овај начин градите заштиту од потенцијално нежељених [споредних ефеката](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), и отварате могућности за нове функције у вашој апликацији, као што је имплементација undo/redo, док истовремено олакшавате отклањање грешака. На пример, могли бисте да бележите сваку промену направљену на стању и чувате историју промена како бисте разумели извор грешке.
У JavaScript-у можете користити [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) за креирање непроменљиве верзије објекта. Ако покушате да направите промене у непроменљивом објекту, биће подигнут изузетак.
У JavaScript-у можете користити [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) да креирате непроменљиву верзију објекта. Ако покушате да направите промене на непроменљивом објекту, биће подигнута изузетак.
✅ Знате ли разлику између *плитког* и *дубоког* непроменљивог објекта? Можете прочитати о томе [овде](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Да ли знате разлику између *плитког* и *дубоког* непроменљивог објекта? Можете прочитати о томе [овде](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Задатак
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
У овој функцији креирамо нови објекат стања и копирамо податке из претходног стања користећи [*spread (`...`) оператор*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Затим замењујемо одређено својство објекта стања новим подацима користећи [нотацију са заградама](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` за доделу. На крају, закључавамо објекат како бисмо спречили модификације користећи `Object.freeze()`. Тренутно у стању чувамо само својство `account`, али са овим приступом можете додати онолико својстава колико вам је потребно.
У овој функцији креирамо нови објекат стања и копирамо податке из претходног стања користећи [*оператор ширења (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Затим замењујемо одређено својство објекта стања новим подацима користећи [нотацију са заградама](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` за доделу. На крају, закључавамо објекат како бисмо спречили модификације користећи `Object.freeze()`. Тренутно имамо само својство `account`у стању, али са овим приступом можете додати онолико својстава колико вам је потребноу стање.
Такође ћемо ажурирати иницијализацију `state` како бисмо били сигурни да је почетно стање такође закључано:
Такође ћемо ажурирати иницијализацију `state` како бисмо били сигурни да је почетно стање такође замрзнуто:
```js
let state = Object.freeze({
@ -120,19 +120,19 @@ let state = Object.freeze({
});
```
Након тога, ажурирајте функцију `register`тако што ћете заменити доделу`state.account = result;`са:
Након тога, ажурирајте функцију `register`заменом доделе`state.account = result;`са:
```js
updateState('account', result);
```
Урадите исто са функцијом `login`, заменивши`state.account = data;`са:
Урадите исто са функцијом `login`, заменом`state.account = data;`са:
```js
updateState('account', data);
```
Сада ћемо искористити прилику да решимо проблем са подацима о налогу који се не бришу када корисник кликне на *Одјава*.
Сада ћемо искористити прилику да решимо проблем да подаци о налогу нису очишћени када корисник кликне на *Одјави се*.
Покушајте да региструјете нови налог, одјавите се и поново пријавите како бисте проверили да ли све и даље ради исправно.
Покушајте да региструјете нови налог, одјавите се и поново пријавите да бисте проверили да ли све и даље ради исправно.
> Савет: можете погледати све промене стања додавањем `console.log(state)` на крају `updateState()` и отварањем конзоле у алатима за развој вашег прегледача.
> Савет: можете погледати све промене стања додавањем `console.log(state)` на крај функције`updateState()` и отварањем конзоле у алатима за развој вашег прегледача.
## Чување стања
Већини веб апликација је потребно да чувају податке како би исправно функционисале. Сви критични подаци обично се чувају у бази података и приступа им се преко серверског API-ја, као што су подаци о корисничком налогу у нашем случају. Али понекад је такође занимљиво чувати неке податке на клијентској апликацији која ради у вашем прегледачу, ради бољег корисничког искуства или побољшања перформанси учитавања.
Већини веб апликација је потребно да чувају податке како би исправно функционисале. Сви критични подаци обично се чувају у бази података и приступа им се преко серверског API-ја, као што су подаци о корисничком налогу у нашем случају. Али понекад је такође занимљиво чувати неке податке на клијентској апликацији која ради у вашем прегледачу, ради бољег корисничког искуства или ради побољшања перформанси учитавања.
Када желите да чувате податке у вашем прегледачу, постоји неколико важних питања која треба да поставите себи:
- *Да ли су подаци осетљиви?* Требало би избегавати чување било каквих осетљивих података на клијенту, као што су корисничке лозинке.
- *Колико дуго вам је потребно да задржите ове податке?* Да ли планирате да приступате овим подацима само током тренутне сесије или желите да их чувате заувек?
- *Да ли су подаци осетљиви?* Требало би да избегавате чување било каквих осетљивих података на клијенту, као што су корисничке лозинке.
- *Колико дуго вам је потребно да задржите ове податке?* Да ли планирате да приступите овим подацима само током тренутне сесије или желите да их чувате заувек?
Постоји више начина за чување информација унутар веб апликације, у зависности од тога шта желите да постигнете. На пример, можете користити URL-ове за чување упита за претрагу и учинити их доступним за дељење између корисника. Такође можете користити [HTTP колачиће](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ако подаци треба да се деле са сервером, као што су информације о [аутентификацији](https://en.wikipedia.org/wiki/Authentication).
Постоји више начина за чување информација унутар веб апликације, у зависности од тога шта желите да постигнете. На пример, можете користити URL-ове за чување упита за претрагу и учинити их деливим између корисника. Такође можете користити [HTTP колачиће](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ако подаци треба да се деле са сервером, као што су информације о [аутентификацији](https://en.wikipedia.org/wiki/Authentication).
Друга опција је коришћење једног од многих API-ја прегледача за чување података. Два су посебно занимљива:
Друга опција је коришћење једног од многих API-ја прегледача за чување података. Два од њих су посебно занимљива:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value складиште](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) које омогућава чување података специфичних за тренутни веб сајт током различитих сесија. Подаци сачувани у њему никада не истичу.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ово ради исто као `localStorage`, осим што се подаци сачувани у њему бришу кадасе сесија заврши (када се прегледач затвори).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) који омогућава чување података специфичних за тренутни веб сајт током различитих сесија. Подаци сачувани у њему никада не истичу.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): овај ради исто као `localStorage` осим што се подаци сачувани у њему бришу када сесија заврши (када се прегледач затвори).
Имајте на уму да оба ова API-ја дозвољавају чување само [стрингова](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ако желите да чувате сложене објекте, мораћете да их серијализујете у [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) формат користећи [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Ако желите да направите веб апликацију која не ради са сервером, могуће је направити базу података на клијенту користећи [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Овоје резервисано за напредне случајеве употребе или ако треба да чувате значајну количину података, јерје сложеније за коришћење.
✅ Ако желите да креирате веб апликацију која не ради са сервером, могуће је креирати базу података на клијенту користећи [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Овајје резервисан за напредне случајеве употребе или ако треба да чувате значајну количину података, јерје сложенији за употребу.
### Задатак
Желимо да наши корисници остану пријављени док експлицитно не кликну на дугме *Одјава*, па ћемо користити `localStorage` за чување података о налогу. Прво, хајде да дефинишемо кључ који ћемо користити за чување наших података.
Желимо да наши корисници остану пријављени док експлицитно не кликну на дугме *Одјави се*, па ћемо користити `localStorage` за чување података о налогу. Прво, хајде да дефинишемо кључ који ћемо користити за чување наших података.
Са овим, подаци о корисничком налогу ће бити сачувани и увек ажурирани јер смо претходно централизовали сва наша ажурирања стања. Овде почињемо да уживамо у предностима свих наших претходних рефакторисања 🙂.
Са овим, подаци о корисничком налогу ће бити сачувани и увек ажурирани,јер смо претходно централизовали сва наша ажурирања стања. Овде почињемо да уживамо у предностима свих наших претходних рефакторисања 🙂.
Како су подаци сачувани, такође морамо водити рачуна о њиховом враћању када се апликација учита. Пошто ћемо почети да имамо више кода за иницијализацију, можда је добра идеја да направимо нову функцију `init`, која такође укључује наш претходни код на крају `app.js`:
Како су подаци сачувани, такође морамо водити рачуна о њиховом враћању када се апликација учита. Пошто ћемо почети да имамо више кода за иницијализацију, можда је добра идеја да креирамо нову функцију `init`, која такође укључује наш претходни код на крају `app.js`:
```js
function init() {
@ -204,13 +204,13 @@ init();
Овде враћамо сачуване податке, и ако их има, ажурирамо стање у складу с тим. Важно је то урадити *пре* ажурирања руте, јер може постојати код који се ослања на стање током ажурирања странице.
Такође можемо направити страницу *Контролна табла* подразумеваном страницом наше апликације, јер сада чувамо податке о налогу. Ако подаци нису пронађени, контролна табла се брине о преусмеравању на страницу *Пријава*. У`updateRoute()`, замените резервну `return navigate('/login');`са`return navigate('/dashboard');`.
Такође можемо учинити страницу *Контролна табла* подразумеваном страницом наше апликације, јер сада чувамо податке о налогу. Ако подаци нису пронађени, контролна табла се брине о преусмеравању на страницу *Пријава*. Уфункцији `updateRoute()`, замените резервну опцију`return navigate('/login');`са`return navigate('/dashboard');`.
Сада се пријавите у апликацију и покушајте да освежите страницу. Требало би да останете на контролној табли. Саовим ажурирањем решили смо све наше почетне проблеме...
Сада се пријавите у апликацију и покушајте да освежите страницу. Требало би да останете на контролној табли. Сатим ажурирањем решили смо све наше почетне проблеме...
## Освежавање података
...Али можда смо створили нови проблем. Опа!
...Али можда смо такође створили нови проблем. Опа!
Идите на контролну таблу користећи налог `test`, а затим покрените ову команду у терминалу да бисте креирали нову трансакцију:
@ -223,7 +223,7 @@ curl --request POST \
Покушајте да освежите страницу контролне табле у прегледачу сада. Шта се дешава? Да ли видите нову трансакцију?
Стање је сачувано на неодређено време захваљујући `localStorage`, али то такође значи да се никада не ажурира док се не одјавите из апликације и поново пријавите!
Стање се чува на неодређено време захваљујући `localStorage`, али то такође значи да се никада не ажурира док се не одјавите из апликације и поново пријавите!
Једна могућа стратегија за решавање овог проблема је да се подаци о налогу поново учитавају сваки пут када се контролна табла учита, како би се избегли застарели подаци.
@ -249,7 +249,7 @@ async function updateAccountData() {
Ова метода проверава да ли сте тренутно пријављени, а затим поново учитава податке о налогу са сервера.
Креирајте још једну функцију под називом `refresh`:
[Квиз након предавања](https://ff-quizzes.netlify.app/web/quiz/48)
Evo primera rezultata nakon završetka zadatka:
## Задатак


---
**Одрицање од одговорности**:
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције [Co-op Translator](https://github.com/Azure/co-op-translator). Иако тежимо тачности, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква неспоразумевања или погрешна тумачења која могу произаћи из коришћења овог превода.
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције [Co-op Translator](https://github.com/Azure/co-op-translator). Иако тежимо тачности, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква погрешна тумачења или неспоразуме који могу настати услед коришћења овог превода.
När en webbapplikation växer blir det en utmaning att hålla reda på alla dataflöden. Vilken kod hämtar datan, vilken sida använder den, var och när behöver den uppdateras...det är lätt att hamna med rörig kod som är svår att underhålla. Detta är särskilt sant när du behöver dela data mellan olika sidor i din app, till exempel användardata. Konceptet *tillståndshantering* har alltid funnits i alla typer av program, men eftersom webbappar blir alltmer komplexa är det nu en nyckelfråga att tänka på under utvecklingen.
När en webbapplikation växer blir det en utmaning att hålla reda på alla dataflöden. Vilken kod hämtar datan, vilken sida använder den, var och när behöver den uppdateras...det är lätt att hamna med rörig kod som är svår att underhålla. Detta är särskilt sant när du behöver dela data mellan olika sidor i din app, till exempel användardata. Konceptet *state management* har alltid funnits i alla typer av program, men eftersom webbappar fortsätter att öka i komplexitet är det nu en viktig punkt att tänka på under utvecklingen.
I denna sista del kommer vi att granska appen vi byggt för att omvärdera hur tillstånd hanteras, vilket möjliggör stöd för att uppdatera webbläsaren när som helst och bevara data mellan användarsessioner.
I denna sista del kommer vi att granska appen vi byggde för att ompröva hur staten hanteras, vilket möjliggör stöd för webbläsaruppdatering när som helst och att data kvarstår över användarsessioner.
### Förutsättningar
Du behöver ha slutfört [datahämtning](../3-data/README.md)-delen av webbappen för denna lektion. Du behöver också installera [Node.js](https://nodejs.org) och [köra server-API:t](../api/README.md) lokalt så att du kan hantera kontodata.
Du måste ha slutfört [datahämtning](../3-data/README.md)-delen av webbappen för denna lektion. Du måste också installera [Node.js](https://nodejs.org) och [köra server-API:t](../api/README.md) lokalt så att du kan hantera kontodata.
Du kan testa att servern fungerar korrekt genom att köra detta kommando i en terminal:
Du kan testa att servern körs korrekt genom att köra detta kommando i en terminal:
```sh
curl http://localhost:5000/api
@ -32,32 +32,32 @@ curl http://localhost:5000/api
---
## Omvärdera tillståndshantering
## Ompröva state management
I [föregående lektion](../3-data/README.md) introducerade vi ett grundläggande koncept för tillstånd i vår app med den globala variabeln `account` som innehåller bankdatan för den inloggade användaren. Men vår nuvarande implementation har vissa brister. Försök att uppdatera sidan när du är på instrumentpanelen. Vad händer?
I [föregående lektion](../3-data/README.md) introducerade vi ett grundläggande koncept för state i vår app med den globala variabeln `account` som innehåller bankdata för den aktuella inloggade användaren. Men vår nuvarande implementation har vissa brister. Försök att uppdatera sidan när du är på instrumentpanelen. Vad händer?
Det finns tre problem med den nuvarande koden:
- Tillståndet sparas inte, eftersom en uppdatering av webbläsaren tar dig tillbaka till inloggningssidan.
- Det finns flera funktioner som ändrar tillståndet. När appen växer kan det bli svårt att hålla reda på ändringarna och lätt att glömma att uppdatera något.
- Tillståndet rensas inte, så när du klickar på *Logga ut* finns kontodatan fortfarande kvar även om du är på inloggningssidan.
- Staten sparas inte, eftersom en webbläsaruppdatering tar dig tillbaka till inloggningssidan.
- Det finns flera funktioner som ändrar staten. När appen växer kan det bli svårt att spåra ändringarna och det är lätt att glömma att uppdatera en.
- Staten rensas inte, så när du klickar på *Logga ut* finns kontodata fortfarande kvar även om du är på inloggningssidan.
Vi skulle kunna uppdatera vår kod för att lösa dessa problem ett i taget, men det skulle skapa mer kodupprepning och göra appen mer komplex och svår att underhålla. Eller så kan vi pausa i några minuter och omvärdera vår strategi.
Vi skulle kunna uppdatera vår kod för att hantera dessa problem ett i taget, men det skulle skapa mer kodduplicering och göra appen mer komplex och svår att underhålla. Eller så kan vi pausa några minuter och ompröva vår strategi.
> Vilka problem försöker vi egentligen lösa här?
[Tillståndshantering](https://en.wikipedia.org/wiki/State_management) handlar om att hitta ett bra sätt att lösa dessa två specifika problem:
[State management](https://en.wikipedia.org/wiki/State_management) handlar om att hitta en bra metod för att lösa dessa två specifika problem:
- Hur håller vi dataflödena i en app begripliga?
- Hur håller vi tillståndsdata alltid synkroniserade med användargränssnittet (och vice versa)?
- Hur håller man dataflödena i en app begripliga?
- Hur håller man state-data alltid synkroniserad med användargränssnittet (och vice versa)?
När du har tagit hand om dessa kan andra problem antingen redan vara lösta eller bli enklare att lösa. Det finns många möjliga tillvägagångssätt för att lösa dessa problem, men vi kommer att använda en vanlig lösning som består av att **centralisera datan och sätten att ändra den**. Dataflödena skulle se ut så här:
När du har tagit hand om dessa kan eventuella andra problem du har antingen redan vara lösta eller ha blivit enklare att lösa. Det finns många möjliga metoder för att lösa dessa problem, men vi kommer att använda en vanlig lösning som består av att **centralisera data och sätt att ändra den**. Dataflödena skulle se ut så här:


> Vi kommer inte att täcka delen där data automatiskt uppdaterar vyn, eftersom det är kopplat till mer avancerade koncept inom [Reaktiv Programmering](https://en.wikipedia.org/wiki/Reactive_programming). Det är ett bra ämne att fördjupa sig i om du vill gå djupare.
> Vi kommer inte att täcka den del där data automatiskt triggar uppdatering av vyn, eftersom det är kopplat till mer avancerade koncept inom [Reaktiv Programmering](https://en.wikipedia.org/wiki/Reactive_programming). Det är ett bra ämne att fördjupa sig i om du är intresserad.
✅ Det finns många bibliotek där ute med olika tillvägagångssätt för tillståndshantering, [Redux](https://redux.js.org) är ett populärt alternativ. Ta en titt på koncepten och mönstren som används, eftersom det ofta är ett bra sätt att lära sig vilka potentiella problem du kan stöta på i stora webbappar och hur de kan lösas.
✅ Det finns många bibliotek där ute med olika metoder för state management, [Redux](https://redux.js.org) är ett populärt alternativ. Ta en titt på koncepten och mönstren som används eftersom det ofta är ett bra sätt att lära sig vilka potentiella problem du kan stöta på i stora webbappar och hur de kan lösas.
### Uppgift
@ -75,9 +75,9 @@ let state = {
};
```
Tanken är att *centralisera* all vår appdata i ett enda tillståndsobjekt. Vi har bara `account` för tillfället i tillståndet, så det förändrar inte mycket, men det skapar en väg för framtida utveckling.
Idén är att *centralisera* all vår appdata i ett enda state-objekt. Vi har bara `account` för tillfället i staten så det förändrar inte mycket, men det skapar en väg för framtida utveckling.
Vi måste också uppdatera funktionerna som använder det. I funktionerna `register()` och `login()`, ersätt `account = ...` med `state.account = ...`;
Vi måste också uppdatera funktionerna som använder det. I funktionerna `register()` och `login()` ersätt `account = ...` med `state.account = ...`;
Lägg till denna rad högst upp i funktionen `updateDashboard()`:
@ -85,17 +85,17 @@ Lägg till denna rad högst upp i funktionen `updateDashboard()`:
const account = state.account;
```
Denna omstrukturering i sig gav inte så stora förbättringar, men tanken var att lägga grunden för de kommande ändringarna.
Denna omstrukturering i sig gav inte mycket förbättringar, men idén var att lägga grunden för nästa förändringar.
## Spåra dataändringar
Nu när vi har infört objektet `state` för att lagra vår data är nästa steg att centralisera uppdateringarna. Målet är att göra det enklare att hålla reda på alla ändringar och när de sker.
Nu när vi har infört `state`-objektet för att lagra vår data är nästa steg att centralisera uppdateringarna. Målet är att göra det enklare att hålla reda på alla ändringar och när de sker.
För att undvika att ändringar görs direkt i objektet `state` är det också en bra praxis att betrakta det som [*oföränderligt*](https://en.wikipedia.org/wiki/Immutable_object), vilket innebär att det inte kan ändras alls. Det innebär också att du måste skapa ett nytt tillståndsobjekt om du vill ändra något i det. Genom att göra detta bygger du ett skydd mot potentiellt oönskade [bieffekter](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) och öppnar upp möjligheter för nya funktioner i din app, som att implementera ångra/gör om, samtidigt som det blir enklare att felsöka. Till exempel kan du logga varje ändring som görs i tillståndet och hålla en historik över ändringarna för att förstå källan till ett fel.
För att undvika att ändringar görs direkt i `state`-objektet är det också en bra praxis att betrakta det som [*oföränderligt*](https://en.wikipedia.org/wiki/Immutable_object), vilket innebär att det inte kan ändras alls. Det innebär också att du måste skapa ett nytt state-objekt om du vill ändra något i det. Genom att göra detta bygger du ett skydd mot potentiellt oönskade [bieffekter](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) och öppnar upp möjligheter för nya funktioner i din app, som att implementera ångra/göra om, samtidigt som det blir enklare att felsöka. Till exempel kan du logga varje ändring som görs i staten och hålla en historik över ändringarna för att förstå källan till ett fel.
I JavaScript kan du använda [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) för att skapa en oföränderlig version av ett objekt. Om du försöker göra ändringar i ett oföränderligt objekt kommer ett undantag att kastas.
✅ Vet du skillnaden mellan ett *ytligt* och ett *djupt* oföränderligt objekt? Du kan läsa om det [här](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Vet du skillnaden mellan ett *ytligt* och ett *djupgående* oföränderligt objekt? Du kan läsa om det [här](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Uppgift
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
I denna funktion skapar vi ett nytt tillståndsobjekt och kopierar data från det tidigare tillståndet med hjälp av [*spridningsoperatorn (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Sedan skriver vi över en specifik egenskap i tillståndsobjektet med den nya datan med hjälp av [haknotationen](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` för tilldelning. Slutligen låser vi objektet för att förhindra ändringar med `Object.freeze()`. Vi har bara egenskapen `account` lagrad i tillståndet för tillfället, men med detta tillvägagångssätt kan du lägga till så många egenskaper som du behöver i tillståndet.
I denna funktion skapar vi ett nytt state-objekt och kopierar data från det tidigare state med hjälp av [*spridningsoperatorn (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Sedan skriver vi över en specifik egenskap i state-objektet med den nya datan med hjälp av [haknotationen](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` för tilldelning. Slutligen låser vi objektet för att förhindra ändringar med `Object.freeze()`. Vi har bara egenskapen `account` lagrad i staten för tillfället, men med denna metod kan du lägga till så många egenskaper som du behöver i staten.
Vi uppdaterar också initialiseringen av `state` för att säkerställa att det initiala tillståndet också är fryst:
Vi kommer också att uppdatera initialiseringen av `state` för att säkerställa att det initiala state är fryst också:
```js
let state = Object.freeze({
@ -132,7 +132,7 @@ Gör samma sak med funktionen `login`, ersätt `state.account = data;` med:
updateState('account', data);
```
Vi passar nu på att lösa problemet med att kontodatan inte rensas när användaren klickar på *Logga ut*.
Vi tar nu tillfället i akt att lösa problemet med att kontodata inte rensas när användaren klickar på *Logga ut*.
Skapa en ny funktion `logout()`:
@ -143,35 +143,35 @@ function logout() {
}
```
I `updateDashboard()`, ersätt omdirigeringen `return navigate('/login');` med `return logout();`.
I `updateDashboard()`, ersätt omdirigeringen `return navigate('/login');` med `return logout();`
Försök att registrera ett nytt konto, logga ut och logga in igen för att kontrollera att allt fortfarande fungerar korrekt.
> Tips: Du kan titta på alla tillståndsändringar genom att lägga till `console.log(state)` längst ner i `updateState()` och öppna konsolen i din webbläsares utvecklingsverktyg.
> Tips: du kan titta på alla state-ändringar genom att lägga till `console.log(state)` längst ner i `updateState()` och öppna konsolen i din webbläsares utvecklingsverktyg.
## Bevara tillståndet
## Spara staten
De flesta webbappar behöver bevara data för att fungera korrekt. All kritisk data lagras vanligtvis i en databas och nås via ett server-API, som användarkontodata i vårt fall. Men ibland är det också intressant att bevara viss data i klientappen som körs i din webbläsare, för en bättre användarupplevelse eller för att förbättra laddningsprestandan.
De flesta webbappar behöver spara data för att kunna fungera korrekt. All kritisk data lagras vanligtvis i en databas och nås via ett server-API, som användarkontodata i vårt fall. Men ibland kan det också vara intressant att spara viss data i klientappen som körs i din webbläsare, för en bättre användarupplevelse eller för att förbättra laddningsprestandan.
När du vill bevara data i din webbläsare finns det några viktiga frågor du bör ställa dig:
När du vill spara data i din webbläsare finns det några viktiga frågor du bör ställa dig:
- *Är datan känslig?* Du bör undvika att lagra känslig data på klienten, som användarlösenord.
- *Hur länge behöver du behålla denna data?* Planerar du att använda denna data endast under den aktuella sessionen eller vill du att den ska lagras för alltid?
- *Är datan känslig?* Du bör undvika att lagra känslig data på klienten, såsom användarlösenord.
- *Hur länge behöver du behålla denna data?* Planerar du att använda denna data endast för den aktuella sessionen eller vill du att den ska lagras för alltid?
Det finns flera sätt att lagra information i en webbapp, beroende på vad du vill uppnå. Till exempel kan du använda URL:er för att lagra en sökfråga och göra den delbar mellan användare. Du kan också använda [HTTP-cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) om datan behöver delas med servern, som [autentiseringsinformation](https://en.wikipedia.org/wiki/Authentication).
Ett annat alternativ är att använda en av de många webbläsar-API:erna för att lagra data. Två av dem är särskilt intressanta:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): en [Nyckel/Värde-lagring](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) som gör det möjligt att bevara data specifik för den aktuella webbplatsen mellan olika sessioner. Datan som sparas i den upphör aldrig att gälla.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): en [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) som gör det möjligt att spara data specifik för den aktuella webbplatsen över olika sessioner. Datan som sparas i den upphör aldrig.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): denna fungerar på samma sätt som `localStorage` förutom att datan som lagras i den rensas när sessionen avslutas (när webbläsaren stängs).
Observera att båda dessa API:er endast tillåter lagring av [strängar](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Om du vill lagra komplexa objekt måste du serialisera dem till [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-formatet med [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Observera att båda dessa API:er endast tillåter att lagra [strängar](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Om du vill lagra komplexa objekt måste du serialisera dem till [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)-formatet med [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Om du vill skapa en webbapp som inte fungerar med en server är det också möjligt att skapa en databas på klienten med hjälp av [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Detta är reserverat för avancerade användningsfall eller om du behöver lagra en betydande mängd data, eftersom det är mer komplext att använda.
✅ Om du vill skapa en webbapp som inte fungerar med en server är det också möjligt att skapa en databas på klienten med [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Detta är reserverat för avancerade användningsfall eller om du behöver lagra betydande mängder data, eftersom det är mer komplext att använda.
### Uppgift
Vi vill att våra användare ska förbli inloggade tills de uttryckligen klickar på knappen *Logga ut*, så vi kommer att använda `localStorage` för att lagra kontodatan. Först definierar vi en nyckel som vi kommer att använda för att lagra vår data.
Vi vill att våra användare ska förbli inloggade tills de uttryckligen klickar på *Logga ut*-knappen, så vi kommer att använda `localStorage` för att lagra kontodata. Först, låt oss definiera en nyckel som vi kommer att använda för att lagra vår data.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Lägg sedan till denna rad i slutet av funktionen `updateState()`:
Med detta kommer användarkontodatan att bevaras och alltid vara uppdaterad eftersom vi tidigare centraliserade alla våra tillståndsuppdateringar. Det är här vi börjar dra nytta av alla våra tidigare omstruktureringar 🙂.
Med detta kommer användarkontodata att sparas och alltid vara uppdaterad eftersom vi tidigare centraliserade alla våra state-uppdateringar. Det är här vi börjar dra nytta av alla våra tidigare omstruktureringar 🙂.
Eftersom datan sparas måste vi också ta hand om att återställa den när appen laddas. Eftersom vi börjar få mer initialiseringskod kan det vara en bra idé att skapa en ny funktion `init`, som också inkluderar vår tidigare kod längst ner i `app.js`:
Eftersom datan sparas måste vi också ta hand om att återställa den när appen laddas. Eftersom vi börjar få mer initialiseringskod kan det vara en bra idé att skapa en ny `init`-funktion, som också inkluderar vår tidigare kod längst ner i `app.js`:
```js
function init() {
@ -202,9 +202,9 @@ function init() {
init();
```
Här hämtar vi den sparade datan, och om det finns någon uppdaterar vi tillståndet därefter. Det är viktigt att göra detta *innan* vi uppdaterar routen, eftersom det kan finnas kod som förlitar sig på tillståndet under siduppdateringen.
Här hämtar vi den sparade datan, och om det finns någon uppdaterar vi staten därefter. Det är viktigt att göra detta *innan* vi uppdaterar routen, eftersom det kan finnas kod som förlitar sig på staten under siduppdateringen.
Vi kan också göra *Instrumentpanelen* till vår applikations standardsida, eftersom vi nu bevarar kontodatan. Om ingen data hittas tar instrumentpanelen hand om att omdirigera till *Inloggningssidan* ändå. I `updateRoute()`, ersätt fallbacken `return navigate('/login');` med `return navigate('/dashboard');`.
Vi kan också göra *Dashboard*-sidan till vår applikations standardsida, eftersom vi nu sparar kontodata. Om ingen data hittas tar instrumentpanelen hand om att omdirigera till *Login*-sidan ändå. I `updateRoute()`, ersätt fallbacken `return navigate('/login');` med `return navigate('/dashboard');`.
Logga nu in i appen och försök att uppdatera sidan. Du bör stanna kvar på instrumentpanelen. Med den uppdateringen har vi tagit hand om alla våra ursprungliga problem...
Försök att uppdatera instrumentpanelen i webbläsaren nu. Vad händer? Ser du den nya transaktionen?
Försök att uppdatera din instrumentpanelsida i webbläsaren nu. Vad händer? Ser du den nya transaktionen?
Tillståndet bevaras på obestämd tid tack vare `localStorage`, men det betyder också att det aldrig uppdateras förrän du loggar ut ur appen och loggar in igen!
Staten sparas på obestämd tid tack vare `localStorage`, men det betyder också att den aldrig uppdateras förrän du loggar ut från appen och loggar in igen!
En möjlig strategi för att lösa detta är att ladda om kontodatan varje gång instrumentpanelen laddas, för att undvika föråldrad data.
@ -258,7 +258,7 @@ async function refresh() {
}
```
Denna funktion uppdaterar kontodatan och tar sedan hand om att uppdatera HTML:en på instrumentpanelsidan. Det är vad vi behöver kalla på när routen för instrumentpanelen laddas. Uppdatera routedefinitionen med:
Denna uppdaterar kontodatan och tar sedan hand om att uppdatera HTML:en på instrumentpanelsidan. Det är vad vi behöver kalla på när instrumentpanelens route laddas. Uppdatera routedefinitionen med:
```js
const routes = {
@ -273,22 +273,22 @@ Försök att ladda om instrumentpanelen nu, den bör visa den uppdaterade kontod
## 🚀 Utmaning
Nu när vi laddar om kontodatan varje gång instrumentpanelen laddas, tror du att vi fortfarande behöver bevara *all kontodata*?
Nu när vi laddar om kontodatan varje gång instrumentpanelen laddas, tror du att vi fortfarande behöver spara *hela kontodatan*?
Försök att arbeta tillsammans för att ändra vad som sparas och laddas från `localStorage`så att det endast inkluderar det som är absolut nödvändigt för att appen ska fungera.
Försök att arbeta tillsammans för att ändra vad som sparas och laddas från `localStorage`till att endast inkludera det som är absolut nödvändigt för att appen ska fungera.
[Quiz efter föreläsningen](https://ff-quizzes.netlify.app/web/quiz/48)
## Uppgift
[Implementera dialogrutan "Lägg till transaktion"](assignment.md)
Här är ett exempelresultat efter att uppgiften har slutförts:
[Implementera dialogen "Lägg till transaktion"](assignment.md)
Här är ett exempel på resultat efter att ha slutfört uppgiften:


---
**Ansvarsfriskrivning**:
Detta dokument har översatts med hjälp av AI-översättningstjänsten [Co-op Translator](https://github.com/Azure/co-op-translator). Även om vi strävar efter noggrannhet, bör du vara medveten om att automatiserade översättningar kan innehålla fel eller inexaktheter. Det ursprungliga dokumentet på dess ursprungliga språk bör betraktas som den auktoritativa källan. För kritisk information rekommenderas professionell mänsklig översättning. Vi ansvarar inte för eventuella missförstånd eller feltolkningar som uppstår vid användning av denna översättning.
Detta dokument har översatts med hjälp av AI-översättningstjänsten [Co-op Translator](https://github.com/Azure/co-op-translator). Även om vi strävar efter noggrannhet, bör det noteras att automatiserade översättningar kan innehålla fel eller felaktigheter. Det ursprungliga dokumentet på dess originalspråk bör betraktas som den auktoritativa källan. För kritisk information rekommenderas professionell mänsklig översättning. Vi ansvarar inte för eventuella missförstånd eller feltolkningar som uppstår vid användning av denna översättning.
Kadri programu ya wavuti inavyokua, inakuwa changamoto kufuatilia mtiririko wa data zote. Ni msimbo gani unaopata data, ni ukurasa gani unaoitumia, ni wapi na lini inahitaji kusasishwa...ni rahisi kuishia na msimbo usioeleweka ambao ni mgumu kudumisha. Hii ni kweli hasa unapohitaji kushiriki data kati ya kurasa tofauti za programu yako, kwa mfano data za mtumiaji. Dhana ya *usimamizi wa hali* imekuwepo kila wakati katika aina zote za programu, lakini kadri programu za wavuti zinavyozidi kuwa ngumu, sasa ni jambo muhimu kufikiria wakati wa maendeleo.
Kadri programu ya wavuti inavyokua, inakuwa changamoto kufuatilia mtiririko wa data zote. Ni msimbo gani unapata data, ni ukurasa gani unaitumia, wapi na lini inahitaji kusasishwa...ni rahisi kuishia na msimbo usioeleweka ambao ni mgumu kudumisha. Hii ni kweli hasa unapohitaji kushiriki data kati ya kurasa tofauti za programu yako, kwa mfano data ya mtumiaji. Dhana ya *usimamizi wa hali* imekuwepo daima katika aina zote za programu, lakini kadri programu za wavuti zinavyokua kwa ugumu, sasa ni jambo muhimu kufikiria wakati wa maendeleo.
Katika sehemu hii ya mwisho, tutapitia programu tuliyojenga ili kufikiria upya jinsi hali inavyosimamiwa, kuruhusu msaada wa kuhuisha kivinjari wakati wowote, na kuhifadhi data katika vipindi vya mtumiaji.
### Mahitaji ya Awali
Unahitaji kuwa umekamilisha sehemu ya [kupata data](../3-data/README.md) ya programu ya wavuti kwa somo hili. Pia unahitaji kusakinisha [Node.js](https://nodejs.org) na [kuendesha API ya seva](../api/README.md) kwa ndani ili uweze kusimamia data za akaunti.
Unahitaji kuwa umekamilisha sehemu ya [kupata data](../3-data/README.md) ya programu ya wavuti kwa somo hili. Pia unahitaji kusakinisha [Node.js](https://nodejs.org) na [kuendesha API ya seva](../api/README.md) kwa ndani ili uweze kusimamia data ya akaunti.
Unaweza kujaribu kama seva inafanya kazi vizuri kwa kutekeleza amri hii kwenye terminal:
Unaweza kujaribu kama seva inaendesha vizuri kwa kutekeleza amri hii kwenye terminal:
```sh
curl http://localhost:5000/api
@ -34,34 +34,34 @@ curl http://localhost:5000/api
## Fikiria Upya Usimamizi wa Hali
Katika [somo lililopita](../3-data/README.md), tulianzisha dhana ya msingi ya hali katika programu yetu kwa kutumia kigezo cha kimataifa `account` ambacho kina data za benki za mtumiaji aliyesajiliwa kwa sasa. Hata hivyo, utekelezaji wetu wa sasa una kasoro kadhaa. Jaribu kuhuisha ukurasa ukiwa kwenye dashibodi. Nini kinatokea?
Katika [somo lililopita](../3-data/README.md), tulianzisha dhana ya msingi ya hali katika programu yetu na kigezo cha kimataifa `account` ambacho kina data ya benki ya mtumiaji aliyesajiliwa kwa sasa. Hata hivyo, utekelezaji wetu wa sasa una kasoro kadhaa. Jaribu kuhuisha ukurasa ukiwa kwenye dashibodi. Nini kinatokea?
Kuna masuala matatu na msimbo wa sasa:
Kuna masuala 3 na msimbo wa sasa:
- Hali haijahifadhiwa, kwani kuhuisha kivinjari kunakurudisha kwenye ukurasa wa kuingia.
- Kuna kazi nyingi zinazobadilisha hali. Kadri programu inavyokua, inaweza kuwa vigumu kufuatilia mabadiliko na ni rahisi kusahau kusasisha moja.
- Hali haijasafishwa, kwa hivyo unapobofya *Ondoka* data za akaunti bado zipo ingawa uko kwenye ukurasa wa kuingia.
- Kuna kazi nyingi zinazobadilisha hali. Kadri programu inavyokua, inaweza kufanya iwe vigumu kufuatilia mabadiliko na ni rahisi kusahau kusasisha moja.
- Hali haijasafishwa, kwa hivyo unapobofya *Ondoka* data ya akaunti bado ipo ingawa uko kwenye ukurasa wa kuingia.
Tunaweza kusasisha msimbo wetu kushughulikia masuala haya moja baada ya jingine, lakini ingeunda urudufu wa msimbo zaidi na kufanya programu kuwa ngumu zaidi na ngumu kudumisha. Au tunaweza kusimama kwa dakika chache na kufikiria upya mkakati wetu.
Tunaweza kusasisha msimbo wetu kushughulikia masuala haya moja baada ya jingine, lakini ingeunda kurudia msimbo zaidi na kufanya programu kuwa ngumu zaidi kudumisha. Au tunaweza kusimama kwa dakika chache na kufikiria upya mkakati wetu.
> Ni matatizo gani hasa tunajaribu kutatua hapa?
> Ni matatizo gani tunajaribu kutatua hapa?
[Usimamizi wa hali](https://en.wikipedia.org/wiki/State_management) unahusu kupata mbinu nzuri ya kutatua matatizo haya mawili maalum:
- Jinsi ya kuweka mtiririko wa data katika programu ueleweke?
- Jinsi ya kuweka data za hali kila wakati zilingane na kiolesura cha mtumiaji (na kinyume chake)?
- Jinsi ya kuweka mtiririko wa data katika programu uwe rahisi kueleweka?
- Jinsi ya kuweka data ya hali daima inalingana na kiolesura cha mtumiaji (na kinyume chake)?
Ukishughulikia haya, masuala mengine yoyote unayoweza kuwa nayo yanaweza kuwa tayari yametatuliwa au yamekuwa rahisi kutatua. Kuna mbinu nyingi zinazowezekana za kutatua matatizo haya, lakini tutatumia suluhisho la kawaida linalojumuisha **kuzingatia data na njia za kuibadilisha**. Mtiririko wa data ungekuwa hivi:
Ukishughulikia haya, masuala mengine yoyote unayoweza kuwa nayo yanaweza kuwa yameshughulikiwa tayari au yamekuwa rahisi kutatua. Kuna mbinu nyingi zinazowezekana za kutatua matatizo haya, lakini tutatumia suluhisho la kawaida linalojumuisha **kuzingatia data na njia za kuibadilisha**. Mtiririko wa data ungekuwa kama huu:


> Hatutashughulikia hapa sehemu ambapo data inachochea kiolesura cha mtazamo kiotomatiki, kwani inahusiana na dhana za hali ya juu za [Programu ya Majibu](https://en.wikipedia.org/wiki/Reactive_programming). Ni somo zuri la kufuatilia ikiwa uko tayari kwa uchambuzi wa kina.
> Hatutashughulikia hapa sehemu ambapo data inasababisha kiolesura cha mtumiaji kusasishwa kiotomatiki, kwani inahusiana na dhana za juu zaidi za [Programu ya Kijibu](https://en.wikipedia.org/wiki/Reactive_programming). Ni somo zuri la kufuatilia ikiwa uko tayari kuchimba kwa kina.
✅ Kuna maktaba nyingi huko nje zenye mbinu tofauti za usimamizi wa hali, [Redux](https://redux.js.org) ikiwa chaguo maarufu. Angalia dhana na mifumo inayotumika kwani mara nyingi ni njia nzuri ya kujifunza masuala gani ya uwezo unaweza kukabiliana nayo katika programu kubwa za wavuti na jinsi yanavyoweza kutatuliwa.
✅ Kuna maktaba nyingi huko nje zenye mbinu tofauti za usimamizi wa hali, [Redux](https://redux.js.org) ikiwa chaguo maarufu. Angalia dhana na mifumo inayotumika kwani mara nyingi ni njia nzuri ya kujifunza masuala yanayoweza kutokea katika programu kubwa za wavuti na jinsi yanavyoweza kutatuliwa.
### Kazi
Tutaanza na mabadiliko kidogo ya msimbo. Badilisha tangazo la `account`:
Tutaanza na marekebisho kidogo. Badilisha tamko la `account`:
```js
let account = null;
@ -75,9 +75,9 @@ let state = {
};
```
Wazo ni *kuzingatia* data zote za programu yetu katika kitu kimoja cha hali. Kwa sasa tuna `account` pekee katika hali kwa hivyo haibadilishi mengi, lakini inatoa njia ya mabadiliko.
Wazo ni *kuzingatia* data yote ya programu yetu katika kitu kimoja cha hali. Kwa sasa tuna `account` pekee katika hali kwa hivyo haibadiliki sana, lakini inaunda njia ya mabadiliko.
Pia tunapaswa kusasisha kazi zinazotumia. Katika kazi za `register()` na `login()`, badilisha `account = ...` na `state.account = ...`;
Pia tunahitaji kusasisha kazi zinazotumia. Katika kazi za `register()` na `login()`, badilisha `account = ...` na `state.account = ...`;
Kwenye mwanzo wa kazi ya `updateDashboard()`, ongeza mstari huu:
@ -85,13 +85,13 @@ Kwenye mwanzo wa kazi ya `updateDashboard()`, ongeza mstari huu:
const account = state.account;
```
Mabadiliko haya yenyewe hayajaleta maboresho makubwa, lakini wazo lilikuwa ni kuweka msingi wa mabadiliko yanayofuata.
Marekebisho haya yenyewe hayajaleta maboresho makubwa, lakini wazo lilikuwa kuweka msingi wa mabadiliko yanayofuata.
## Fuatilia Mabadiliko ya Data
Sasa kwa kuwa tumeweka kitu cha `state` kuhifadhi data zetu, hatua inayofuata ni kuzingatia masasisho. Lengo ni kufanya iwe rahisi kufuatilia mabadiliko yoyote na wakati yanapotokea.
Sasa kwa kuwa tumeweka kitu cha `state` kuhifadhi data yetu, hatua inayofuata ni kuzingatia masasisho. Lengo ni kufanya iwe rahisi kufuatilia mabadiliko yoyote na wakati yanapotokea.
Ili kuepuka kuwa na mabadiliko yanayofanywa kwenye kitu cha `state`, pia ni mazoea mazuri kuzingatia kuwa ni [*isiyobadilika*](https://en.wikipedia.org/wiki/Immutable_object), ikimaanisha kuwa haiwezi kubadilishwa kabisa. Hii pia inamaanisha kuwa lazima uunde kitu kipya cha hali ikiwa unataka kubadilisha chochote ndani yake. Kwa kufanya hivyo, unajenga ulinzi dhidi ya [athari zisizotarajiwa](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), na kufungua uwezekano wa vipengele vipya katika programu yako kama kutekeleza undo/redo, huku pia ikifanya iwe rahisi kutatua matatizo. Kwa mfano, unaweza kuandika kila mabadiliko yaliyofanywa kwenye hali na kuweka historia ya mabadiliko ili kuelewa chanzo cha hitilafu.
Ili kuepuka mabadiliko kufanywa kwenye kitu cha `state`, pia ni mazoea mazuri kuzingatia kuwa ni [*kisichobadilika*](https://en.wikipedia.org/wiki/Immutable_object), kumaanisha kuwa hakiwezi kubadilishwa kabisa. Hii pia inamaanisha kuwa lazima uunde kitu kipya cha hali ikiwa unataka kubadilisha chochote ndani yake. Kwa kufanya hivyo, unajenga ulinzi dhidi ya [athari za kando](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) zisizotakiwa, na kufungua uwezekano wa vipengele vipya katika programu yako kama kutekeleza undo/redo, huku pia ukifanya iwe rahisi kutatua hitilafu. Kwa mfano, unaweza kuandika kila mabadiliko yaliyofanywa kwenye hali na kuweka historia ya mabadiliko ili kuelewa chanzo cha hitilafu.
Katika JavaScript, unaweza kutumia [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) kuunda toleo lisilobadilika la kitu. Ukijaribu kufanya mabadiliko kwenye kitu kisichobadilika, kutatokea hitilafu.
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
Katika kazi hii, tunaunda kitu kipya cha hali na kunakili data kutoka hali ya awali kwa kutumia [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kisha tunabadilisha mali fulani ya kitu cha hali kwa data mpya kwa kutumia [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` kwa ugawaji. Hatimaye, tunafungia kitu ili kuzuia mabadiliko kwa kutumia `Object.freeze()`. Kwa sasa tuna mali ya `account` pekee iliyohifadhiwa katika hali, lakini kwa mbinu hii unaweza kuongeza mali nyingi unavyohitaji katika hali.
Katika kazi hii, tunaunda kitu kipya cha hali na kunakili data kutoka hali ya awali kwa kutumia [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kisha tunabadilisha mali fulani ya kitu cha hali na data mpya kwa kutumia [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` kwa ugawaji. Hatimaye, tunafungia kitu ili kuzuia mabadiliko kwa kutumia `Object.freeze()`. Kwa sasa tuna mali ya `account` pekee iliyohifadhiwa katika hali, lakini kwa mbinu hii unaweza kuongeza mali nyingi unavyohitaji katika hali.
Tutabadilisha pia uanzishaji wa `state` ili kuhakikisha hali ya awali pia imefungwa:
Tutasasisha pia uanzishaji wa `state` ili kuhakikisha hali ya awali imefungwa pia:
```js
let state = Object.freeze({
@ -132,7 +132,7 @@ Fanya vivyo hivyo na kazi ya `login`, ukibadilisha `state.account = data;` na:
updateState('account', data);
```
Tutachukua nafasi hii kurekebisha suala la data za akaunti kutosafishwa wakati mtumiaji anabofya *Ondoka*.
Sasa tutachukua nafasi ya kutatua suala la data ya akaunti kutosafishwa wakati mtumiaji anabofya *Ondoka*.
Unda kazi mpya `logout()`:
@ -145,33 +145,33 @@ function logout() {
Katika `updateDashboard()`, badilisha uelekezaji `return navigate('/login');` na `return logout()`;
Jaribu kusajili akaunti mpya, kutoka na kuingia tena ili kuhakikisha kila kitu bado kinafanya kazi vizuri.
Jaribu kusajili akaunti mpya, kutoka nje na kuingia tena ili kuhakikisha kila kitu bado kinafanya kazi vizuri.
> Kidokezo: unaweza kuangalia mabadiliko yote ya hali kwa kuongeza `console.log(state)`mwishoni mwa `updateState()` na kufungua console katika zana za maendeleo za kivinjari chako.
> Kidokezo: unaweza kuangalia mabadiliko yote ya hali kwa kuongeza `console.log(state)`chini ya `updateState()` na kufungua console katika zana za maendeleo za kivinjari chako.
## Hifadhi Hali
Programu nyingi za wavuti zinahitaji kuhifadhi data ili kufanya kazi kwa usahihi. Data zote muhimu kwa kawaida huhifadhiwa kwenye hifadhidata na kupatikana kupitia API ya seva, kama vile data za akaunti ya mtumiaji katika kesi yetu. Lakini wakati mwingine, ni muhimu pia kuhifadhi data fulani kwenye programu ya mteja inayofanya kazi kwenye kivinjari chako, kwa uzoefu bora wa mtumiaji au kuboresha utendaji wa upakiaji.
Programu nyingi za wavuti zinahitaji kuhifadhi data ili ziweze kufanya kazi vizuri. Data zote muhimu kwa kawaida huhifadhiwa kwenye hifadhidata na kufikiwa kupitia API ya seva, kama data ya akaunti ya mtumiaji katika kesi yetu. Lakini wakati mwingine, pia ni muhimu kuhifadhi data fulani kwenye programu ya mteja inayotumika kwenye kivinjari chako, kwa uzoefu bora wa mtumiaji au kuboresha utendaji wa kupakia.
Unapotaka kuhifadhi data kwenye kivinjari chako, kuna maswali machache muhimu unayopaswa kujiuliza:
Unapotaka kuhifadhi data kwenye kivinjari chako, kuna maswali machache muhimu unapaswa kujiuliza:
- *Je, data ni nyeti?* Unapaswa kuepuka kuhifadhi data yoyote nyeti kwenye mteja, kama vile nywila za mtumiaji.
- *Unahitaji kuhifadhi data hii kwa muda gani?* Je, unapanga kufikia data hii tu kwa kikao cha sasa au unataka ihifadhiwe milele?
- *Je, data ni nyeti?* Unapaswa kuepuka kuhifadhi data yoyote nyeti kwenye mteja, kama nywila za mtumiaji.
- *Kwa muda gani unahitaji kuhifadhi data hii?* Je, unapanga kufikia data hii tu kwa kipindi cha sasa au unataka ihifadhiwe milele?
Kuna njia nyingi za kuhifadhi taarifa ndani ya programu ya wavuti, kulingana na kile unachotaka kufanikisha. Kwa mfano, unaweza kutumia URL kuhifadhi swali la utafutaji, na kuifanya iweze kushirikiwa kati ya watumiaji. Unaweza pia kutumia [vidakuzi vya HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ikiwa data inahitaji kushirikiwa na seva, kama vile taarifa za [uthibitishaji](https://en.wikipedia.org/wiki/Authentication).
Kuna njia nyingi za kuhifadhi taarifa ndani ya programu ya wavuti, kulingana na kile unachotaka kufanikisha. Kwa mfano, unaweza kutumia URL kuhifadhi swala la utafutaji, na kuifanya iweze kushirikiwa kati ya watumiaji. Unaweza pia kutumia [vidakuzi vya HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) ikiwa data inahitaji kushirikiwa na seva, kama taarifa za [uthibitishaji](https://en.wikipedia.org/wiki/Authentication).
Chaguo jingine ni kutumia mojawapo ya API nyingi za kivinjari za kuhifadhi data. Mbili kati ya hizo ni za kuvutia hasa:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): hifadhi ya [Key/Value](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) inayoruhusu kuhifadhi data maalum kwa tovuti ya sasa katika vipindi tofauti. Data iliyohifadhiwa ndani yake haimalizi muda wake.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): hii inafanya kazi sawa na `localStorage` isipokuwa kwamba data iliyohifadhiwa ndani yake inafutwa wakati kikao kinapoisha (wakati kivinjari kinapofungwa).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): hifadhi ya [Key/Value](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) inayoruhusu kuhifadhi data maalum kwa tovuti ya sasa katika vipindi tofauti. Data iliyohifadhiwa ndani yake haimaliziki.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): hii inafanya kazi sawa na `localStorage` isipokuwa kwamba data iliyohifadhiwa ndani yake inafutwa wakati kipindi kinamalizika (wakati kivinjari kinapofungwa).
Kumbuka kuwa API hizi mbili huruhusu tu kuhifadhi [nyuzi](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ikiwa unataka kuhifadhi vitu tata, utahitaji kuvisarifu kwa umbizo la [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) kwa kutumia [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Kumbuka kuwa API hizi mbili huruhusu tu kuhifadhi [nyuzi](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Ikiwa unataka kuhifadhi vitu tata, utahitaji kuvisarifu kwa muundo wa [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) kwa kutumia [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Ikiwa unataka kuunda programu ya wavuti isiyofanya kazi na seva, pia inawezekana kuunda hifadhidata kwenye mteja kwa kutumia [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Hii inahifadhiwa kwa kesi za matumizi ya hali ya juu au ikiwa unahitaji kuhifadhi kiasi kikubwa cha data, kwani ni ngumu zaidi kutumia.
✅ Ikiwa unataka kuunda programu ya wavuti ambayo haifanyi kazi na seva, pia inawezekana kuunda hifadhidata kwenye mteja kwa kutumia [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Hii inahifadhiwa kwa matumizi ya juu au ikiwa unahitaji kuhifadhi kiasi kikubwa cha data, kwani ni ngumu zaidi kutumia.
### Kazi
Tunataka watumiaji wetu waendelee kuwa wameingia hadi watakapobofya waziwazi kitufe cha *Ondoka*, kwa hivyo tutatumia `localStorage` kuhifadhi data za akaunti. Kwanza, hebu tueleze ufunguo ambao tutatumia kuhifadhi data zetu.
Tunataka watumiaji wetu wabaki wameingia hadi watakapobofya kitufe cha *Ondoka* waziwazi, kwa hivyo tutatumia `localStorage` kuhifadhi data ya akaunti. Kwanza, hebu tueleze ufunguo ambao tutatumia kuhifadhi data yetu.
```js
const storageKey = 'savedAccount';
@ -183,9 +183,9 @@ Kisha ongeza mstari huu mwishoni mwa kazi ya `updateState()`:
Kwa hili, data za akaunti ya mtumiaji zitahifadhiwa na kila wakati zitakuwa za kisasa kwani tulizingatia hapo awali masasisho yetu yote ya hali. Hapa ndipo tunapoanza kufaidika na mabadiliko yetu yote ya awali 🙂.
Kwa hili, data ya akaunti ya mtumiaji itahifadhiwa na daima itakuwa ya kisasa kama tulivyozingatia hapo awali masasisho yote ya hali yetu. Hapa ndipo tunaanza kufaidika na marekebisho yetu ya awali 🙂.
Kwa kuwa data imehifadhiwa, pia tunapaswa kushughulikia urejeshaji wake wakati programu inapopakuliwa. Kwa kuwa tutaanza kuwa na msimbo zaidi wa uanzishaji, inaweza kuwa wazo nzuri kuunda kazi mpya ya `init`, ambayo pia inajumuisha msimbo wetu wa awali mwishoni mwa `app.js`:
Kwa kuwa data imehifadhiwa, tunapaswa pia kushughulikia kuirejesha wakati programu inapopakuliwa. Kwa kuwa tutaanza kuwa na msimbo zaidi wa uanzishaji, inaweza kuwa wazo nzuri kuunda kazi mpya ya `init`, ambayo pia inajumuisha msimbo wetu wa awali chini ya `app.js`:
```js
function init() {
@ -202,9 +202,9 @@ function init() {
init();
```
Hapa tunarejesha data iliyohifadhiwa, na ikiwa ipo tunasasisha hali ipasavyo. Ni muhimu kufanya hivi *kabla* ya kusasisha njia, kwani kunaweza kuwa na msimbo unaotegemea hali wakati wa sasisho la ukurasa.
Hapa tunarejesha data iliyohifadhiwa, na ikiwa kuna yoyote tunasasisha hali ipasavyo. Ni muhimu kufanya hivi *kabla* ya kusasisha njia, kwani kunaweza kuwa na msimbo unaotegemea hali wakati wa sasisho la ukurasa.
Tunaweza pia kufanya ukurasa wa *Dashibodi* kuwa ukurasa chaguo-msingi wa programu yetu, kwani sasa tunahifadhi data za akaunti. Ikiwa hakuna data inayopatikana, dashibodi inashughulikia kuelekeza kwenye ukurasa wa *Kuingia* hata hivyo. Katika `updateRoute()`, badilisha chaguo-msingi`return navigate('/login');` na `return navigate('/dashboard');`.
Tunaweza pia kufanya ukurasa wa *Dashibodi* kuwa ukurasa chaguo-msingi wa programu yetu, kwani sasa tunahifadhi data ya akaunti. Ikiwa hakuna data inayopatikana, dashibodi inashughulikia kuelekeza kwenye ukurasa wa *Kuingia* hata hivyo. Katika `updateRoute()`, badilisha uelekezaji wa`return navigate('/login');` na `return navigate('/dashboard');`.
Sasa ingia kwenye programu na jaribu kuhuisha ukurasa. Unapaswa kubaki kwenye dashibodi. Kwa sasisho hilo tumeshughulikia masuala yetu yote ya awali...
@ -225,7 +225,7 @@ Jaribu kuhuisha ukurasa wa dashibodi kwenye kivinjari sasa. Nini kinatokea? Je,
Hali imehifadhiwa milele shukrani kwa `localStorage`, lakini hiyo pia inamaanisha kuwa haijasasishwa kamwe hadi utoke kwenye programu na kuingia tena!
Mkakati mmoja unaowezekana wa kurekebisha hilo ni kupakia tena data za akaunti kila wakati dashibodi inapopakuliwa, ili kuepuka data zilizokwama.
Mkakati mmoja unaowezekana wa kutatua hilo ni kupakia tena data ya akaunti kila wakati dashibodi inapopakuliwa, ili kuepuka data iliyokwama.
### Kazi
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Njia hii inakagua ikiwa tumeingia kwa sasa kisha inapakia tena data za akaunti kutoka kwa seva.
Njia hii inakagua kwamba kwa sasa tumeingia kisha inapakia tena data ya akaunti kutoka kwa seva.
Unda kazi nyingine inayoitwa `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Hii inasasisha data za akaunti, kisha inashughulikia kusasisha HTML ya ukurasa wa dashibodi. Hii ndiyo tunahitaji kuita wakati njia ya dashibodi inapopakuliwa. Sasisha ufafanuzi wa njia na:
Hii inasasisha data ya akaunti, kisha inashughulikia kusasisha HTML ya ukurasa wa dashibodi. Ni kile tunachohitaji kuita wakati njia ya dashibodi inapopakuliwa. Sasisha ufafanuzi wa njia na:
```js
const routes = {
@ -267,22 +267,22 @@ const routes = {
};
```
Jaribu kuhuisha dashibodi sasa, inapaswa kuonyesha data za akaunti zilizosasishwa.
Jaribu kupakia tena dashibodi sasa, inapaswa kuonyesha data ya akaunti iliyosasishwa.
---
## 🚀 Changamoto
Sasa kwa kuwa tunapakia tena data za akaunti kila wakati dashibodi inapopakuliwa, je, unafikiri bado tunahitaji kuhifadhi *data zote za akaunti*?
Sasa kwa kuwa tunapakia tena data ya akaunti kila wakati dashibodi inapopakuliwa, je, unadhani bado tunahitaji kuhifadhi *data yote ya akaunti*?
Jaribu kufanya kazi pamoja kubadilisha kile kinachohifadhiwa na kupakiwa kutoka `localStorage` ili kujumuisha tu kile kinachohitajika kabisa kwa programu kufanya kazi.
## Maswali ya Baada ya Somo
[Maswali ya baada ya somo](https://ff-quizzes.netlify.app/web/quiz/48)
[Jaribio la baada ya mhadhara](https://ff-quizzes.netlify.app/web/quiz/48)
## Kazi
[Tekeleza "Ongeza muamala" kisanduku cha mazungumzo](assignment.md)
[Tekeleza kisanduku cha mazungumzo cha "Ongeza muamala"](assignment.md)
Hapa kuna mfano wa matokeo baada ya kukamilisha kazi:
@ -291,4 +291,4 @@ Hapa kuna mfano wa matokeo baada ya kukamilisha kazi:
---
**Kanusho**:
Hati hii imetafsiriwa kwa kutumia huduma ya tafsiri ya AI [Co-op Translator](https://github.com/Azure/co-op-translator). Ingawa tunajitahidi kwa usahihi, tafadhali fahamu kuwa tafsiri za kiotomatiki zinaweza kuwa na makosa au kutokuwa sahihi. Hati ya asili katika lugha yake ya awali inapaswa kuzingatiwa kama chanzo cha mamlaka. Kwa taarifa muhimu, inashauriwa kutumia huduma ya tafsiri ya kitaalamu ya binadamu. Hatutawajibika kwa maelewano mabaya au tafsiri zisizo sahihi zinazotokana na matumizi ya tafsiri hii.
Hati hii imetafsiriwa kwa kutumia huduma ya tafsiri ya AI [Co-op Translator](https://github.com/Azure/co-op-translator). Ingawa tunajitahidi kuhakikisha usahihi, tafadhali fahamu kuwa tafsiri za kiotomatiki zinaweza kuwa na makosa au kutokuwa sahihi. Hati ya asili katika lugha yake ya awali inapaswa kuzingatiwa kama chanzo cha mamlaka. Kwa taarifa muhimu, tafsiri ya kitaalamu ya binadamu inapendekezwa. Hatutawajibika kwa kutoelewana au tafsiri zisizo sahihi zinazotokana na matumizi ya tafsiri hii.
Habang lumalaki ang isang web application, nagiging hamon ang pagsubaybay sa lahat ng daloy ng data. Aling code ang kumukuha ng data, anong pahina ang gumagamit nito, saan at kailan ito kailangang i-update...madaling magresulta sa magulong code na mahirap i-maintain. Lalo na kung kailangan mong magbahagi ng data sa iba't ibang pahina ng iyong app, tulad ng user data. Ang konsepto ng *state management* ay matagal nang umiiral sa lahat ng uri ng programa, ngunit habang patuloy na lumalaki ang pagiging kumplikado ng mga web app, ito ay naging mahalagang aspeto na dapat isaalang-alang sa panahon ng development.
Habang lumalaki ang isang web application, nagiging hamon ang pagsubaybay sa lahat ng daloy ng data. Aling code ang kumukuha ng data, anong pahina ang gumagamit nito, saan at kailan ito kailangang i-update...madaling mauwi sa magulong code na mahirap i-maintain. Lalo na kung kailangan mong ibahagi ang data sa iba't ibang pahina ng iyong app, tulad ng user data. Ang konsepto ng *state management* ay palaging umiiral sa lahat ng uri ng programa, ngunit habang patuloy na lumalago ang pagiging kumplikado ng mga web app, ito ay nagiging mahalagang aspeto na dapat isaalang-alang sa panahon ng development.
Sa huling bahaging ito, susuriin natin ang app na ginawa natin upang muling pag-isipan kung paano pinamamahalaan ang state, na nagbibigay-daan sa suporta para sa pag-refresh ng browser sa anumang punto, at pagpapanatili ng data sa mga session ng user.
Sa huling bahaging ito, susuriin natin ang app na ating ginawa upang muling pag-isipan kung paano pinamamahalaan ang estado, na nagbibigay-daan sa suporta para sa pag-refresh ng browser sa anumang punto, at pagpapanatili ng data sa buong sesyon ng user.
### Paunang Kailangan
### Kinakailangan
Kailangan mong matapos ang [data fetching](../3-data/README.md) na bahagi ng web app para sa araling ito. Kailangan mo ring i-install ang [Node.js](https://nodejs.org) at [patakbuhin ang server API](../api/README.md) nang lokal upang ma-manage ang account data.
Kailangan mong makumpleto ang bahagi ng [data fetching](../3-data/README.md) ng web app para sa araling ito. Kailangan mo ring mag-install ng [Node.js](https://nodejs.org) at [patakbuhin ang server API](../api/README.md) nang lokal upang ma-manage ang account data.
Maaari mong subukan kung gumagana nang maayos ang server sa pamamagitan ng pag-execute ng command na ito sa terminal:
Maaari mong subukan kung maayos na tumatakbo ang server sa pamamagitan ng pag-execute ng command na ito sa terminal:
```sh
curl http://localhost:5000/api
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## Muling Pag-isipan ang State Management
Sa [nakaraang aralin](../3-data/README.md), ipinakilala natin ang pangunahing konsepto ng state sa ating app gamit ang global na `account` variable na naglalaman ng bank data para sa kasalukuyang naka-login na user. Gayunpaman, ang kasalukuyang implementasyon natin ay may ilang mga kahinaan. Subukan mong i-refresh ang pahina habang nasa dashboard ka. Ano ang nangyayari?
Sa [nakaraang aralin](../3-data/README.md), ipinakilala natin ang isang pangunahing konsepto ng estado sa ating app gamit ang global na `account` variable na naglalaman ng bank data para sa kasalukuyang naka-login na user. Gayunpaman, ang kasalukuyang implementasyon natin ay may ilang mga kakulangan. Subukang i-refresh ang pahina habang nasa dashboard ka. Ano ang nangyayari?
Mayroong 3 isyu sa kasalukuyang code:
- Ang state ay hindi napapanatili, dahil ang pag-refresh ng browser ay ibabalik ka sa login page.
- Maraming mga function ang nagbabago sa state. Habang lumalaki ang app, maaaring maging mahirap subaybayan ang mga pagbabago at madaling makalimutan ang pag-update ng isa.
- Ang state ay hindi nalilinis, kaya kapag nag-click ka sa *Logout*, ang account data ay nananatili kahit na nasa login page ka na.
- Ang estado ay hindi napapanatili, dahil ang pag-refresh ng browser ay ibinabalik ka sa login page.
- Maraming mga function ang nagmo-modify ng estado. Habang lumalaki ang app, maaaring maging mahirap subaybayan ang mga pagbabago at madaling makalimutang i-update ang isa.
- Ang estado ay hindi nalilinis, kaya kapag nag-click ka sa *Logout*, ang account data ay naroon pa rin kahit na nasa login page ka na.
Maaari nating i-update ang ating code upang harapin ang mga isyung ito isa-isa, ngunit magreresulta ito sa mas maraming code duplication at gagawing mas kumplikado at mahirap i-maintain ang app. O maaari tayong mag-pause ng ilang minuto at muling pag-isipan ang ating estratehiya.
Maaari nating i-update ang ating code upang harapin ang mga isyung ito isa-isa, ngunit ito ay magdudulot ng higit pang pag-uulit ng code at gagawing mas kumplikado at mahirap i-maintain ang app. O maaari tayong huminto ng ilang minuto at muling pag-isipan ang ating estratehiya.
> Anong mga problema ang talagang sinusubukan nating lutasin dito?
Ang [State management](https://en.wikipedia.org/wiki/State_management) ay tungkol sa paghahanap ng magandang paraan upang lutasin ang dalawang partikular na problema:
Ang [State management](https://en.wikipedia.org/wiki/State_management) ay tungkol sa paghahanap ng magandang paraan upang lutasin ang dalawang partikular na problemang ito:
- Paano mapapanatili ang daloy ng data sa isang app na madaling maunawaan?
- Paano mapapanatili ang state data na palaging naka-sync sa user interface (at vice versa)?
- Paano mapapanatiling madaling maunawaan ang daloy ng data sa isang app?
- Paano mapapanatiling naka-sync ang state data sa user interface (at vice versa)?
Kapag naayos mo na ang mga ito, ang anumang iba pang isyu na maaaring mayroon ka ay maaaring nalutas na o naging mas madali nang lutasin. Maraming posibleng paraan para lutasin ang mga problemang ito, ngunit gagamit tayo ng karaniwang solusyon na binubuo ng **pag-centralize ng data at ng mga paraan para baguhin ito**. Ang daloy ng data ay magiging ganito:
Kapag naayos mo na ang mga ito, ang anumang iba pang isyu na maaaring mayroon ka ay maaaring nalutas na o naging mas madaling ayusin. Maraming posibleng paraan upang lutasin ang mga problemang ito, ngunit gagamit tayo ng isang karaniwang solusyon na binubuo ng **pagse-centralize ng data at ng mga paraan upang baguhin ito**. Ang daloy ng data ay magiging ganito:


> Hindi natin tatalakayin dito ang bahagi kung saan ang data ay awtomatikong nagti-trigger ng view update, dahil ito ay konektado sa mas advanced na mga konsepto ng [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). Magandang follow-up na paksa ito kung handa kang mag-dive nang mas malalim.
> Hindi natin tatalakayin dito ang bahagi kung saan ang data ay awtomatikong nagti-trigger ng pag-update ng view, dahil ito ay konektado sa mas advanced na mga konsepto ng [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). Magandang follow-up na paksa ito kung nais mong mag-dive nang mas malalim.
✅ Maraming mga library ang may iba't ibang approach sa state management, [Redux](https://redux.js.org) ang isa sa mga sikat na opsyon. Tingnan ang mga konsepto at pattern na ginagamit nito dahil madalas itong magandang paraan upang matutunan ang mga potensyal na isyu na maaaring harapin sa malalaking web app at kung paano ito malulutas.
✅ Maraming mga library na may iba't ibang diskarte sa state management, ang [Redux](https://redux.js.org) ay isang sikat na opsyon. Tingnan ang mga konsepto at pattern na ginagamit nito dahil madalas itong magandang paraan upang matutunan ang mga potensyal na isyu na maaari mong harapin sa malalaking web app at kung paano ito malulutas.
### Gawain
@ -67,7 +67,7 @@ Magsisimula tayo sa kaunting refactoring. Palitan ang deklarasyon ng `account`:
let account = null;
```
Gamit:
Ng:
```js
let state = {
@ -75,7 +75,7 @@ let state = {
};
```
Ang ideya ay *i-centralize* ang lahat ng data ng ating app sa isang state object. Sa ngayon, mayroon lamang tayong `account` sa state kaya hindi ito masyadong nagbabago, ngunit nagbibigay ito ng daan para sa mga susunod na pagbabago.
Ang ideya ay *i-centralize* ang lahat ng data ng ating app sa isang state object. Sa ngayon, mayroon lamang tayong `account` sa estado kaya't hindi ito masyadong nagbabago, ngunit ito ay nagbubukas ng daan para sa mga susunod na pagbabago.
Kailangan din nating i-update ang mga function na gumagamit nito. Sa mga function na `register()` at `login()`, palitan ang `account = ...` ng `state.account = ...`;
@ -85,17 +85,17 @@ Sa itaas ng function na `updateDashboard()`, idagdag ang linyang ito:
const account = state.account;
```
Ang refactoring na ito ay hindi nagdala ng maraming improvement, ngunit ang ideya ay maglatag ng pundasyon para sa mga susunod na pagbabago.
Ang refactoring na ito mismo ay hindi nagdala ng malaking pagbabago, ngunit ang ideya ay maglatag ng pundasyon para sa mga susunod na pagbabago.
## Subaybayan ang Mga Pagbabago sa Data
Ngayon na nailagay na natin ang `state` object para mag-imbak ng ating data, ang susunod na hakbang ay i-centralize ang mga update. Ang layunin ay gawing mas madali ang pagsubaybay sa anumang mga pagbabago at kung kailan ito nangyayari.
Ngayon na nailagay na natin ang `state` object upang mag-imbak ng ating data, ang susunod na hakbang ay i-centralize ang mga update. Ang layunin ay gawing mas madali ang pagsubaybay sa anumang mga pagbabago at kung kailan ito nangyayari.
Upang maiwasan ang mga pagbabago sa `state` object, magandang practice din na ituring itong [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), ibig sabihin hindi ito maaaring baguhin. Nangangahulugan din ito na kailangan mong gumawa ng bagong state object kung gusto mong baguhin ang anumang bagay dito. Sa paggawa nito, nagtatayo ka ng proteksyon laban sa mga posibleng hindi gustong [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), at nagbubukas ng mga posibilidad para sa mga bagong feature sa iyong app tulad ng pag-implement ng undo/redo, habang pinapadali rin ang pag-debug. Halimbawa, maaari mong i-log ang bawat pagbabago na ginawa sa state at panatilihin ang history ng mga pagbabago upang maunawaan ang pinagmulan ng isang bug.
Upang maiwasan ang mga pagbabago sa `state` object, magandang kasanayan din na ituring itong [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), ibig sabihin ay hindi ito maaaring baguhin. Nangangahulugan din ito na kailangan mong lumikha ng bagong state object kung nais mong baguhin ang anumang bagay dito. Sa paggawa nito, nagtatayo ka ng proteksyon laban sa mga posibleng hindi gustong [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), at nagbubukas ng mga posibilidad para sa mga bagong tampok sa iyong app tulad ng pag-implement ng undo/redo, habang ginagawang mas madali ang pag-debug. Halimbawa, maaari mong i-log ang bawat pagbabago na ginawa sa estado at panatilihin ang kasaysayan ng mga pagbabago upang maunawaan ang pinagmulan ng isang bug.
Sa JavaScript, maaari mong gamitin ang [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) upang lumikha ng immutable na bersyon ng isang object. Kung susubukan mong gumawa ng mga pagbabago sa isang immutable object, magri-raise ng exception.
Sa JavaScript, maaari mong gamitin ang [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) upang lumikha ng isang immutable na bersyon ng isang object. Kung susubukan mong gumawa ng mga pagbabago sa isang immutable na object, isang exception ang itataas.
✅ Alam mo ba ang pagkakaiba ng *shallow* at *deep* immutable object? Maaari mong basahin ito [dito](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Alam mo ba ang pagkakaiba ng isang *shallow* at isang*deep* immutable object? Maaari mong basahin ito [dito](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Gawain
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
Sa function na ito, gumagawa tayo ng bagong state object at kinokopya ang data mula sa nakaraang state gamit ang [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Pagkatapos, pinapalitan natin ang partikular na property ng state object gamit ang [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para sa assignment. Sa wakas, nilalock natin ang object upang maiwasan ang mga pagbabago gamit ang `Object.freeze()`. Sa ngayon, mayroon lamang tayong `account` property na naka-store sa state, ngunit sa approach na ito maaari kang magdagdag ng maraming property hangga't kailangan mo sa state.
Sa function na ito, lumilikha tayo ng bagong state object at kinokopya ang data mula sa nakaraang estado gamit ang [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Pagkatapos ay pinapalitan natin ang isang partikular na property ng state object gamit ang [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para sa assignment. Sa wakas, nilalock natin ang object upang maiwasan ang mga pagbabago gamit ang `Object.freeze()`. Sa ngayon, mayroon lamang tayong `account` property na nakaimbak sa estado, ngunit sa diskarteng ito maaari kang magdagdag ng maraming property hangga't kailangan mo sa estado.
I-update din natin ang `state` initialization upang matiyak na ang initial state ay frozen din:
I-update din natin ang `state` initialization upang matiyak na ang initial state ay naka-freeze din:
```js
let state = Object.freeze({
@ -126,13 +126,13 @@ Pagkatapos nito, i-update ang `register` function sa pamamagitan ng pagpapalit n
updateState('account', result);
```
Gawin din ito sa `login` function, palitan ang `state.account = data;` ng:
Gawin din ang parehong bagay sa `login` function, palitan ang `state.account = data;` ng:
```js
updateState('account', data);
```
Ngayon, samantalahin natin ang pagkakataon upang ayusin ang isyu ng account data na hindi nalilinis kapag nag-click ang user sa *Logout*.
Ngayon ay samantalahin natin ang pagkakataon upang ayusin ang isyu ng hindi nalilinis na account data kapag nag-click ang user sa *Logout*.
Gumawa ng bagong function na `logout()`:
@ -145,47 +145,47 @@ function logout() {
Sa `updateDashboard()`, palitan ang redirection na `return navigate('/login');` ng `return logout()`;
Subukang magrehistro ng bagong account, mag-logout, at mag-login muli upang suriin kung gumagana pa rin nang maayos ang lahat.
Subukang magrehistro ng bagong account, mag-logout, at mag-login muli upang suriin kung maayos pa rin ang lahat.
> Tip: Maaari mong tingnan ang lahat ng pagbabago sa state sa pamamagitan ng pagdaragdag ng `console.log(state)` sa ibaba ng `updateState()` at pagbukas ng console sa development tools ng iyong browser.
> Tip: maaari mong tingnan ang lahat ng pagbabago sa estado sa pamamagitan ng pagdaragdag ng `console.log(state)` sa ibaba ng `updateState()` at pagbubukas ng console sa development tools ng iyong browser.
## Panatilihin ang State
## Panatilihin ang Estado
Karamihan sa mga web app ay kailangang magpanatili ng data upang gumana nang maayos. Ang lahat ng critical na data ay karaniwang naka-store sa isang database at ina-access sa pamamagitan ng server API, tulad ng user account data sa ating kaso. Ngunit minsan, interesante rin na magpanatili ng ilang data sa client app na tumatakbo sa iyong browser, para sa mas magandang user experience o upang mapabuti ang loading performance.
Karamihan sa mga web app ay kailangang magpanatili ng data upang gumana nang maayos. Ang lahat ng mahalagang data ay karaniwang nakaimbak sa isang database at ina-access sa pamamagitan ng server API, tulad ng user account data sa ating kaso. Ngunit minsan, interesante ring magpanatili ng ilang data sa client app na tumatakbo sa iyong browser, para sa mas magandang karanasan ng user o upang mapabuti ang performance ng pag-load.
Kapag gusto mong magpanatili ng data sa iyong browser, may ilang mahahalagang tanong na dapat mong itanong sa iyong sarili:
Kapag nais mong magpanatili ng data sa iyong browser, may ilang mahahalagang tanong na dapat mong itanong sa iyong sarili:
- *Sensitive ba ang data?* Dapat mong iwasan ang pag-store ng anumang sensitive na data sa client, tulad ng mga password ng user.
- *Gaano katagal mo kailangang panatilihin ang data na ito?* Plano mo bang i-access ang data na ito para lamang sa kasalukuyang session o gusto mo itong i-store magpakailanman?
- *Sensitive ba ang data?* Dapat mong iwasan ang pag-iimbak ng anumang sensitibong data sa client, tulad ng mga password ng user.
- *Gaano katagal mo kailangang panatilihin ang data na ito?* Plano mo bang i-access ang data na ito para lamang sa kasalukuyang sesyon o nais mo itong maiimbak magpakailanman?
Mayroong iba't ibang paraan ng pag-store ng impormasyon sa loob ng isang web app, depende sa kung ano ang gusto mong makamit. Halimbawa, maaari mong gamitin ang URLs upang mag-store ng search query, at gawing shareable ito sa pagitan ng mga user. Maaari mo ring gamitin ang [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) kung ang data ay kailangang i-share sa server, tulad ng [authentication](https://en.wikipedia.org/wiki/Authentication) information.
Mayroong iba't ibang paraan ng pag-iimbak ng impormasyon sa loob ng isang web app, depende sa kung ano ang nais mong makamit. Halimbawa, maaari mong gamitin ang URLs upang mag-imbak ng search query, at gawing shareable ito sa pagitan ng mga user. Maaari mo ring gamitin ang [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) kung ang data ay kailangang ibahagi sa server, tulad ng impormasyon sa [authentication](https://en.wikipedia.org/wiki/Authentication).
Isa pang opsyon ay ang paggamit ng isa sa maraming browser APIs para sa pag-store ng data. Dalawa sa mga ito ang partikular na interesante:
Isa pang opsyon ay ang paggamit ng isa sa maraming browser APIs para sa pag-iimbak ng data. Dalawa sa mga ito ay partikular na interesante:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): isang [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) na nagpapahintulot sa pagpanatili ng data na partikular sa kasalukuyang website sa iba't ibang session. Ang data na naka-save dito ay hindi kailanman nag-e-expire.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ito ay gumagana nang katulad sa `localStorage` maliban na ang data na naka-store dito ay nalilinis kapag natapos ang session (kapag isinara ang browser).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): isang [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) na nagpapahintulot na magpanatili ng data na partikular sa kasalukuyang website sa iba't ibang sesyon. Ang data na naka-save dito ay hindi kailanman nag-e-expire.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ito ay gumagana katulad ng `localStorage` maliban na ang data na nakaimbak dito ay nabubura kapag natapos ang sesyon (kapag isinara ang browser).
Tandaan na ang parehong APIs na ito ay nagpapahintulot lamang sa pag-store ng [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Kung gusto mong mag-store ng complex objects, kailangan mo itong i-serialize sa [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format gamit ang [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Tandaan na ang parehong mga API na ito ay nagpapahintulot lamang na mag-imbak ng [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Kung nais mong mag-imbak ng mga kumplikadong object, kailangan mong i-serialize ito sa [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format gamit ang [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Kung gusto mong gumawa ng web app na hindi gumagana sa server, posible rin na gumawa ng database sa client gamit ang [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ang API na ito ay nakalaan para sa advanced na paggamit o kung kailangan mong mag-store ng malaking dami ng data, dahil mas kumplikado itong gamitin.
✅ Kung nais mong gumawa ng isang web app na hindi gumagana sa isang server, posible ring gumawa ng database sa client gamit ang [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Ang API na ito ay nakalaan para sa mga advanced na kaso ng paggamit o kung kailangan mong mag-imbak ng malaking dami ng data, dahil ito ay mas kumplikado gamitin.
### Gawain
Gusto natin na ang mga user ay manatiling naka-login hanggang sa sila mismo ang mag-click sa *Logout* button, kaya gagamit tayo ng `localStorage` upang i-store ang account data. Una, mag-define tayo ng key na gagamitin natin para i-store ang ating data.
Nais nating manatiling naka-log in ang ating mga user hanggang sa sila mismo ang mag-click sa *Logout* button, kaya gagamit tayo ng `localStorage` upang mag-imbak ng account data. Una, magtakda tayo ng key na gagamitin natin upang mag-imbak ng ating data.
```js
const storageKey = 'savedAccount';
```
Pagkatapos, idagdag ang linyang ito sa dulo ng `updateState()` function:
Pagkatapos ay idagdag ang linyang ito sa dulo ng `updateState()` function:
Sa ganitong paraan, ang user account data ay mapapanatili at palaging up-to-date dahil na-centralize natin ang lahat ng ating state updates. Dito natin sisimulan ang pagkuha ng benepisyo mula sa lahat ng ating mga naunang refactor 🙂.
Sa ganitong paraan, ang user account data ay mapapanatili at palaging up-to-date dahil na-centralize na natin ang lahat ng ating state updates. Dito natin sisimulang maramdaman ang benepisyo ng lahat ng ating mga naunang refactor 🙂.
Dahil ang data ay naka-save, kailangan din nating alagaan ang pag-restore nito kapag ang app ay na-load. Dahil magsisimula tayong magkaroon ng mas maraming initialization code, magandang ideya na gumawa ng bagong `init` function, na kasama rin ang ating naunang code sa ibaba ng `app.js`:
Dahil ang data ay naka-save, kailangan din nating tiyakin na maibabalik ito kapag na-load ang app. Dahil magkakaroon na tayo ng mas maraming initialization code, magandang ideya na gumawa ng bagong `init` function, na kasama rin ang ating naunang code sa ibaba ng `app.js`:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Dito natin kinukuha ang naka-save na data, at kung mayroon, ina-update natin ang state nang naaayon. Mahalagang gawin ito *bago* i-update ang route, dahil maaaring may code na umaasa sa state sa panahon ng page update.
Dito natin kinukuha ang naka-save na data, at kung mayroon man, ina-update natin ang estado nang naaayon. Mahalagang gawin ito *bago* i-update ang ruta, dahil maaaring may code na umaasa sa estado sa panahon ng pag-update ng pahina.
Maaari rin nating gawing default page ng ating application ang *Dashboard* page, dahil ngayon ay pinapanatili na natin ang account data. Kung walang data na natagpuan, ang dashboard ang bahala sa pag-redirect sa *Login* page. Sa `updateRoute()`, palitan ang fallback na `return navigate('/login');` ng `return navigate('/dashboard');`.
Maaari rin nating gawing default na pahina ng ating application ang *Dashboard*, dahil ngayon ay pinapanatili na natin ang account data. Kung walang data na makita, ang dashboard ang bahalang mag-redirect sa *Login* page. Sa `updateRoute()`, palitan ang fallback na `return navigate('/login');` ng `return navigate('/dashboard');`.
Ngayon mag-login sa app at subukang i-refresh ang pahina. Dapat kang manatili sa dashboard. Sa update na ito, naayos na natin ang lahat ng ating mga paunang isyu...
Ngayon mag-login sa app at subukang i-refresh ang pahina. Dapat kang manatili sa dashboard. Sa update na ito, naayos na natin ang lahat ng ating mga unang isyu...
## I-refresh ang Data
...Ngunit maaaring nakagawa rin tayo ng bagong isyu. Oops!
...Ngunit maaaring nakagawa rin tayo ng bago. Oops!
Pumunta sa dashboard gamit ang `test` account, pagkatapos ay patakbuhin ang command na ito sa terminal upang gumawa ng bagong transaction:
Pumunta sa dashboard gamit ang `test` account, pagkatapos ay patakbuhin ang command na ito sa terminal upang lumikha ng bagong transaction:
Subukang i-refresh ang dashboard page sa browser ngayon. Ano ang nangyayari? Nakikita mo ba ang bagong transaction?
Subukang i-refresh ang iyong dashboard page sa browser ngayon. Ano ang nangyayari? Nakikita mo ba ang bagong transaction?
Ang state ay napapanatili nang walang hanggan salamat sa `localStorage`, ngunit nangangahulugan din ito na hindi ito kailanman na-update hanggang sa mag-log out ka sa app at mag-log in muli!
Ang estado ay napapanatili nang walang hanggan salamat sa `localStorage`, ngunit nangangahulugan din ito na hindi ito kailanman na-update hanggang sa mag-log out ka sa app at mag-log in muli!
Isang posibleng estratehiya upang ayusin ito ay ang pag-reload ng account data tuwing na-load ang dashboard, upang maiwasan ang stale data.
Ang isang posibleng estratehiya upang ayusin iyon ay ang i-reload ang account data tuwing na-load ang dashboard, upang maiwasan ang lumang data.
### Gawain
@ -247,7 +247,7 @@ async function updateAccountData() {
}
```
Ang method na ito ay nagche-check kung kasalukuyan kang naka-login, pagkatapos ay nire-reload ang account data mula sa server.
Ang method na ito ay sinusuri kung kasalukuyan kang naka-log in pagkatapos ay nire-reload ang account data mula sa server.
Gumawa ng isa pang function na tinatawag na `refresh`:
@ -258,7 +258,7 @@ async function refresh() {
}
```
Ang function na ito ay nag-a-update ng account data, pagkatapos ay ina-update ang HTML ng dashboard page. Ito ang kailangan nating tawagin kapag na-load ang dashboard route. I-update ang route definition gamit:
Ang function na ito ay nag-a-update ng account data, pagkatapos ay inaasikaso ang pag-update ng HTML ng dashboard page. Ito ang kailangan nating tawagin kapag na-load ang dashboard route. I-update ang route definition gamit ang:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
Subukang i-refresh ang dashboard ngayon, dapat nitong ipakita ang na-update na account data.
Subukang i-reload ang dashboard ngayon, dapat nitong ipakita ang na-update na account data.
---
## 🚀 Hamon
Ngayon na nire-reload natin ang account data tuwing na-load ang dashboard, sa tingin mo ba kailangan pa nating panatilihin *lahat ng account* data?
Ngayon na nire-reload natin ang account data tuwing na-load ang dashboard, sa tingin mo ba kailangan pa rin nating panatilihin ang*lahat ng account* data?
Subukang magtulungan upang baguhin kung ano ang sine-save at niloload mula sa `localStorage` upang isama lamang ang mga bagay na talagang kinakailangan para gumana ang app.
Subukang magtulungan upang baguhin kung ano ang sine-save at niloload mula sa `localStorage` upang isama lamang kung ano ang talagang kinakailangan para gumana ang app.
[Ipapatupad ang "Magdagdag ng transaksyon" na dialog](assignment.md)
Narito ang isang halimbawa ng resulta matapos makumpleto ang gawain:
[Ipapatupad ang "Add transaction" dialog](assignment.md)
Narito ang isang halimbawa ng resulta pagkatapos makumpleto ang takdang-aralin:


---
**Paunawa**:
Ang dokumentong ito ay isinalin gamit ang AI translation service na [Co-op Translator](https://github.com/Azure/co-op-translator). Bagama't sinisikap naming maging tumpak, tandaan na ang mga awtomatikong pagsasalin ay maaaring maglaman ng mga pagkakamali o hindi pagkakatugma. Ang orihinal na dokumento sa kanyang katutubong wika ang dapat ituring na opisyal na sanggunian. Para sa mahalagang impormasyon, inirerekomenda ang propesyonal na pagsasalin ng tao. Hindi kami mananagot sa anumang hindi pagkakaunawaan o maling interpretasyon na dulot ng paggamit ng pagsasaling ito.
Ang dokumentong ito ay isinalin gamit ang AI translation service na [Co-op Translator](https://github.com/Azure/co-op-translator). Bagama't sinisikap naming maging tumpak, pakitandaan na ang mga awtomatikong pagsasalin ay maaaring maglaman ng mga pagkakamali o hindi pagkakatugma. Ang orihinal na dokumento sa orihinal nitong wika ang dapat ituring na opisyal na sanggunian. Para sa mahalagang impormasyon, inirerekomenda ang propesyonal na pagsasalin ng tao. Hindi kami mananagot sa anumang hindi pagkakaunawaan o maling interpretasyon na dulot ng paggamit ng pagsasaling ito.
# Bankacılık Uygulaması Yapımı Bölüm 4: Durum Yönetimi Kavramları
# Bir Bankacılık Uygulaması Oluşturma Bölüm 4: Durum Yönetimi Kavramları
## Ders Öncesi Test
@ -15,7 +15,7 @@ CO_OP_TRANSLATOR_METADATA:
### Giriş
Bir web uygulaması büyüdükçe, tüm veri akışlarını takip etmek zorlaşır. Hangi kod veriyi alıyor, hangi sayfa bunu tüketiyor, nerede ve ne zaman güncellenmesi gerekiyor... Kodun karmaşıklaşması ve bakımının zorlaşması kolaydır. Bu durum, özellikle uygulamanızın farklı sayfaları arasında veri paylaşmanız gerektiğinde, örneğin kullanıcı verileri gibi, daha da belirgin hale gelir. *Durum yönetimi* kavramı her tür programda her zaman var olmuştur, ancak web uygulamaları karmaşıklık açısından büyüdükçe, geliştirme sırasında düşünülmesi gereken önemli bir nokta haline gelmiştir.
Bir web uygulaması büyüdükçe, tüm veri akışlarını takip etmek zorlaşır. Hangi kod veriyi alıyor, hangi sayfa bunu kullanıyor, nerede ve ne zaman güncellenmesi gerekiyor... karmaşık ve zor yönetilebilir bir kod yığınına dönüşmek kolaydır. Bu durum, özellikle uygulamanızın farklı sayfaları arasında veri paylaşmanız gerektiğinde, örneğin kullanıcı verileri gibi, daha da belirgin hale gelir. *Durum yönetimi* kavramı her tür programda her zaman var olmuştur, ancak web uygulamaları karmaşıklık açısından büyüdükçe, artık geliştirme sırasında düşünülmesi gereken önemli bir nokta haline gelmiştir.
Bu son bölümde, oluşturduğumuz uygulamayı gözden geçirerek durumun nasıl yönetildiğini yeniden düşüneceğiz. Bu, tarayıcı yenilemesini herhangi bir noktada desteklemeyi ve kullanıcı oturumları arasında verileri kalıcı hale getirmeyi sağlayacaktır.
@ -32,30 +32,30 @@ curl http://localhost:5000/api
---
## Durum yönetimini yeniden düşünmek
## Durum Yönetimini Yeniden Düşünmek
[Önceki derste](../3-data/README.md), uygulamamızda şu anda oturum açmış kullanıcıya ait banka verilerini içeren global `account` değişkeni ile durumun temel bir kavramını tanıttık. Ancak, mevcut uygulamamızda bazı eksiklikler var. Dashboard sayfasındayken sayfayı yenilemeyi deneyin. Ne oluyor?
[Önceki derste](../3-data/README.md), uygulamamızda şu anda oturum açmış kullanıcıya ait banka verilerini içeren global `account` değişkeni ile temel bir durum kavramını tanıttık. Ancak, mevcut uygulamamızda bazı eksiklikler var. Dashboard sayfasındayken sayfayı yenilemeyi deneyin. Ne oluyor?
Mevcut kodda 3 sorun var:
- Durum kalıcı değil, çünkü tarayıcı yenilemesi sizi giriş sayfasına geri götürüyor.
- Durumu değiştiren birden fazla işlev var. Uygulama büyüdükçe, değişiklikleri takip etmek zorlaşabilir ve birini güncellemeyi unutmak kolaydır.
- Durum temizlenmiyor, bu yüzden *Çıkış Yap* düğmesine tıkladığınızda, giriş sayfasında olmanıza rağmen hesap verileri hala orada duruyor.
- Durum kalıcı değil, tarayıcı yenilemesi sizi giriş sayfasına geri götürüyor.
- Durumu değiştiren birden fazla fonksiyon var. Uygulama büyüdükçe, değişiklikleri takip etmek zorlaşabilir ve birini güncellemeyi unutmak kolaydır.
- Durum temizlenmiyor, bu yüzden *Çıkış Yap* düğmesine tıkladığınızda, giriş sayfasında olsanız bile hesap verileri hala orada duruyor.
Bu sorunları tek tek ele almak için kodumuzu güncelleyebiliriz, ancak bu daha fazla kod tekrarı yaratır ve uygulamayı daha karmaşık ve bakımınızor hale getirir. Ya da birkaç dakika durup stratejimizi yeniden düşünebiliriz.
Bu sorunları tek tek ele almak için kodumuzu güncelleyebiliriz, ancak bu daha fazla kod tekrarı yaratır ve uygulamayı daha karmaşık ve zor yönetilebilir hale getirir. Ya da birkaç dakika durup stratejimizi yeniden düşünebiliriz.
> Burada gerçekten çözmeye çalıştığımız sorunlar nelerdir?
[Durum yönetimi](https://en.wikipedia.org/wiki/State_management), bu iki özel sorunu çözmek için iyi bir yaklaşım bulmakla ilgilidir:
- Uygulamadaki veri akışlarını nasıl anlaşılır hale getirebiliriz?
- Bir uygulamadaki veri akışlarını nasıl anlaşılır hale getirebiliriz?
- Durum verilerini her zaman kullanıcı arayüzüyle (ve tam tersi) nasıl senkronize tutabiliriz?
Bu sorunları çözdükten sonra, karşılaşabileceğiniz diğer sorunlar ya zaten çözülmüş olabilir ya da çözülmesi daha kolay hale gelmiştir. Bu sorunları çözmek için birçok olası yaklaşım vardır, ancak biz **verileri ve bunları değiştirme yollarını merkezileştirme** içeren yaygın bir çözümle ilerleyeceğiz. Veri akışları şu şekilde olur:
Bu sorunları çözdüğünüzde, karşılaşabileceğiniz diğer sorunlar ya zaten çözülmüş olur ya da çözülmesi daha kolay hale gelir. Bu sorunları çözmek için birçok olası yaklaşım vardır, ancak biz **verileri ve bunları değiştirme yollarını merkezileştirme** içeren yaygın bir çözümle ilerleyeceğiz. Veri akışları şu şekilde olur:

> Burada verilerin otomatik olarak görünümü güncelleme kısmını ele almayacağız, çünkü bu daha ileri düzey [Reaktif Programlama](https://en.wikipedia.org/wiki/Reactive_programming) kavramlarına bağlıdır. Derinlemesine bir inceleme yapmak istiyorsanız bu iyi bir takip konusu olabilir.
> Burada verilerin otomatik olarak görünümü güncelleme kısmını ele almayacağız, çünkü bu daha ileri düzey [Reaktif Programlama](https://en.wikipedia.org/wiki/Reactive_programming) kavramlarına bağlıdır. Derinlemesine bir inceleme yapmak istiyorsanız, bu iyi bir takip konusu olabilir.
✅ Durum yönetimi için farklı yaklaşımlara sahip birçok kütüphane var, [Redux](https://redux.js.org) popüler bir seçenek. Büyük web uygulamalarında karşılaşabileceğiniz potansiyel sorunları ve bunların nasıl çözülebileceğini öğrenmek için kullanılan kavramlara ve desenlere göz atabilirsiniz.
@ -67,7 +67,7 @@ Biraz yeniden düzenleme ile başlayacağız. `account` tanımını şu şekilde
let account = null;
```
Şununla değiştirin:
Şununla:
```js
let state = {
@ -75,21 +75,21 @@ let state = {
};
```
Fikir, tüm uygulama verilerimizi tek bir durum nesnesinde *merkezileştirmek*. Şu anda durum içinde yalnızca `account` var, bu yüzden çok fazla değişiklik olmuyor, ancak evrimler için bir yol oluşturuyor.
Buradaki fikir, tüm uygulama verilerimizi tek bir durum nesnesinde *merkezileştirmek*. Şu anda durum içinde yalnızca `account` var, bu yüzden çok fazla değişiklik olmuyor, ancak gelişim için bir yol oluşturuyor.
Bunu kullanan işlevleri de güncellememiz gerekiyor. `register()` ve `login()` işlevlerinde, `account = ...` satırını`state.account = ...` ile değiştirin.
Bunu kullanan fonksiyonları da güncellememiz gerekiyor. `register()` ve `login()` fonksiyonlarında, `account = ...` ifadesini`state.account = ...` ile değiştirin.
`updateDashboard()`işlevinin başına şu satırı ekleyin:
`updateDashboard()`fonksiyonunun başına şu satırı ekleyin:
```js
const account = state.account;
```
Bu yeniden düzenleme tek başına çok fazla iyileştirme sağlamadı, ancak fikir bir sonraki değişiklikler için temel oluşturmaktı.
Bu yeniden düzenleme tek başına çok fazla iyileştirme sağlamadı, ancak sonraki değişiklikler için temel oluşturmayı amaçladı.
## Veri değişikliklerini takip etme
## Veri Değişikliklerini Takip Etmek
Artık verilerimizi depolamak için `state` nesnesini oluşturduğumuza göre, bir sonraki adım güncellemeleri merkezileştirmek. Amaç, herhangi bir değişikliği ve ne zaman gerçekleştiğini takip etmeyi kolaylaştırmaktır.
Artık verilerimizi saklamak için `state` nesnesini oluşturduğumuza göre, bir sonraki adım güncellemeleri merkezileştirmek. Amaç, herhangi bir değişikliği ve ne zaman gerçekleştiğini takip etmeyi kolaylaştırmaktır.
`state` nesnesinde değişiklik yapılmasını önlemek için, onu tamamen [*değişmez*](https://en.wikipedia.org/wiki/Immutable_object) olarak düşünmek de iyi bir uygulamadır, yani hiç değiştirilemez. Bu aynı zamanda, içinde bir şey değiştirmek istiyorsanız yeni bir durum nesnesi oluşturmanız gerektiği anlamına gelir. Bunu yaparak, potansiyel olarak istenmeyen [yan etkilerden](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) korunma sağlar ve uygulamanızda geri al/yeniden yap gibi yeni özellikler uygulama olasılıklarını açar, ayrıca hata ayıklamayı kolaylaştırır. Örneğin, duruma yapılan her değişikliği kaydedebilir ve bir hatanın kaynağını anlamak için değişikliklerin geçmişini tutabilirsiniz.
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
Bu işlevde, önceki durumdan veri kopyalayarak yeni bir durum nesnesi oluşturuyoruz. Ardından, durum nesnesinin belirli bir özelliğini yeni veriyle [köşeli parantez notasyonu](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` kullanarak geçersiz kılıyoruz. Son olarak, `Object.freeze()` kullanarak nesneyi değişikliklere karşı kilitliyoruz. Şu anda durum içinde yalnızca `account` özelliği var, ancak bu yaklaşımla duruma ihtiyaç duyduğunuz kadar çok özellik ekleyebilirsiniz.
Bu fonksiyonda, önceki durumdan veri kopyalayarak yeni bir durum nesnesi oluşturuyoruz ve ardından [köşeli parantez notasyonu](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` kullanarak durum nesnesinin belirli bir özelliğini yeni veriyle değiştiriyoruz. Son olarak, `Object.freeze()` kullanarak nesneyi değişikliklere karşı kilitliyoruz. Şu anda durum içinde yalnızca `account` özelliği saklanıyor, ancak bu yaklaşımla duruma ihtiyaç duyduğunuz kadar çok özellik ekleyebilirsiniz.
Durum başlatmasını da güncelleyeceğiz, böylece başlangıç durumu da donmuş olacak:
Durumun başlangıçta da kilitli olduğundan emin olmak için `state` başlatmasını güncelleyeceğiz:
```js
let state = Object.freeze({
@ -120,13 +120,13 @@ let state = Object.freeze({
});
```
Bundan sonra, `register`işlevini güncelleyerek`state.account = result;` atamasını şu şekilde değiştirin:
Bundan sonra, `register`fonksiyonunda`state.account = result;` atamasını şu şekilde değiştirin:
```js
updateState('account', result);
```
Aynı işlemi `login`işlevi için yapın, `state.account = data;` satırını şu şekilde değiştirin:
Aynı işlemi `login`fonksiyonunda yapın, `state.account = data;` ifadesini şu şekilde değiştirin:
`updateDashboard()` içinde, `return navigate('/login');` yönlendirmesini `return logout();` ile değiştirin.
Yeni bir hesap oluşturmayı, çıkış yapmayı ve tekrar giriş yapmayı deneyerek her şeyin hala düzgün çalıştığını kontrol edin.
Yeni bir hesap kaydetmeyi, çıkış yapmayı ve tekrar giriş yapmayı deneyin, her şeyin hala düzgün çalıştığını kontrol edin.
> İpucu: Tüm durum değişikliklerini görmek için `updateState()`işlevinin altına `console.log(state)` ekleyebilir ve tarayıcınızın geliştirme araçlarındaki konsolu açabilirsiniz.
> İpucu: Tüm durum değişikliklerini görmek için `updateState()`fonksiyonunun altına `console.log(state)` ekleyebilir ve tarayıcınızın geliştirme araçlarındaki konsolu açabilirsiniz.
## Durumu kalıcı hale getirme
## Durumu Kalıcı Hale Getirmek
Çoğu web uygulaması, doğru çalışabilmek için verileri kalıcı hale getirmeye ihtiyaç duyar. Tüm kritik veriler genellikle bir veritabanında saklanır ve bir sunucu API'si aracılığıyla erişilir, bizim durumumuzda olduğu gibi kullanıcı hesap verileri. Ancak bazen, daha iyi bir kullanıcı deneyimi veya yükleme performansını artırmak için tarayıcıda çalışan istemci uygulamasında bazı verileri kalıcı hale getirmek ilginç olabilir.
Çoğu web uygulaması, doğru çalışabilmek için verileri kalıcı hale getirmeye ihtiyaç duyar. Tüm kritik veriler genellikle bir veritabanında saklanır ve bir sunucu API'si aracılığıyla erişilir, örneğin kullanıcı hesap verileri gibi. Ancak bazen, daha iyi bir kullanıcı deneyimi veya yükleme performansını artırmak için tarayıcıda çalışan istemci uygulamasında bazı verileri kalıcı hale getirmek de ilginç olabilir.
Tarayıcınızda veri saklamak istediğinizde, kendinize sormanız gereken birkaç önemli soru vardır:
Tarayıcınızda veri kalıcı hale getirmek istediğinizde, kendinize sormanız gereken birkaç önemli soru vardır:
- *Bu veriyi ne kadar süreyle saklamanız gerekiyor?* Bu veriye yalnızca mevcut oturum için mi erişmeyi planlıyorsunuz yoksa sonsuza kadar mı saklamak istiyorsunuz?
Bir web uygulamasında bilgi saklamanın, ne elde etmek istediğinize bağlı olarak birçok yolu vardır. Örneğin, bir arama sorgusunu saklamak ve bunu kullanıcılar arasında paylaşılabilir hale getirmek için URL'leri kullanabilirsiniz. Ayrıca, [kimlik doğrulama](https://en.wikipedia.org/wiki/Authentication) bilgileri gibi verilerin sunucuyla paylaşılması gerekiyorsa [HTTP çerezlerini](https://developer.mozilla.org/docs/Web/HTTP/Cookies) kullanabilirsiniz.
Bir web uygulamasında bilgi saklamanın, ne elde etmek istediğinize bağlı olarak birçok yolu vardır. Örneğin, bir arama sorgusunu saklamak için URL'leri kullanabilir ve bunu kullanıcılar arasında paylaşılabilir hale getirebilirsiniz. Ayrıca, [kimlik doğrulama](https://en.wikipedia.org/wiki/Authentication) bilgileri gibi verilerin sunucuyla paylaşılması gerekiyorsa [HTTP çerezlerini](https://developer.mozilla.org/docs/Web/HTTP/Cookies) kullanabilirsiniz.
Bir diğer seçenek, veri saklamak için birçok tarayıcı API'sinden birini kullanmaktır. İkisi özellikle ilginçtir:
Veri saklamak için birçok tarayıcı API'si vardır. İkisi özellikle ilginçtir:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): belirli bir web sitesine özgü verileri farklı oturumlar arasında kalıcı hale getiren bir [Anahtar/Değer deposu](https://en.wikipedia.org/wiki/Key%E2%80%93value_database). İçindeki veriler asla sona ermez.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): bu, `localStorage` ile aynı şekilde çalışır, ancak içinde saklanan veriler oturum sona erdiğinde (tarayıcı kapatıldığında) temizlenir.
Her iki API'nin de yalnızca [dizeleri](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) saklamaya izin verdiğini unutmayın. Karmaşık nesneleri saklamak istiyorsanız, bunu [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) kullanarak [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) formatında serileştirmeniz gerekir.
Her iki API'nin de yalnızca [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) saklamaya izin verdiğini unutmayın. Karmaşık nesneleri saklamak istiyorsanız, bunu [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) kullanarak [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) formatında serileştirmeniz gerekir.
✅ Bir sunucuyla çalışmayan bir web uygulaması oluşturmak istiyorsanız, istemcide bir veritabanı oluşturmak için [`IndexedDB` API'sini](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) kullanmak da mümkündür. Bu, ileri düzey kullanım durumları veya önemli miktarda veri saklamanız gerektiğinde ayrılmıştır, çünkü kullanımı daha karmaşıktır.
@ -177,7 +177,7 @@ Kullanıcılarımızın *Çıkış Yap* düğmesine açıkça tıklayana kadar o
const storageKey = 'savedAccount';
```
Ardından `updateState()`işlevinin sonuna şu satırı ekleyin:
Ardından `updateState()`fonksiyonunun sonuna şu satırı ekleyin:
Bununla, kullanıcı hesap verileri kalıcı hale gelecek ve daha önce tüm durum güncellemelerini merkezileştirdiğimiz için her zaman güncel olacak. İşte önceki yeniden düzenlemelerimizin faydalarını görmeye başladığımız yer 🙂.
Veriler kaydedildiği için, uygulama yüklendiğinde bunları geri yüklemekle de ilgilenmemiz gerekiyor. Daha fazla başlatma koduna sahip olmaya başlayacağımız için, `app.js` dosyasının altındaki önceki kodumuzu da içeren yeni bir `init`işlevi oluşturmak iyi bir fikir olabilir:
Veriler kaydedildiği için, uygulama yüklendiğinde bunları geri yüklemekle de ilgilenmemiz gerekiyor. Daha fazla başlatma koduna sahip olmaya başlayacağımız için, `app.js` dosyasının altındaki önceki kodumuzu da içeren yeni bir `init`fonksiyonu oluşturmak iyi bir fikir olabilir:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Burada kaydedilen verileri alıyoruz ve herhangi bir veri varsa durumu buna göre güncelliyoruz. Bunu *sayfayı güncelleme* sırasında duruma güvenen kod olabileceğinden, rotayı güncellemeden *önce* yapmak önemlidir.
Burada kaydedilen verileri alıyoruz ve varsa durumu buna göre güncelliyoruz. Bunu *sayfayı güncellemeden önce* yapmak önemlidir, çünkü sayfa güncellemesi sırasında duruma güvenen kodlar olabilir.
Artık *Dashboard* sayfasını uygulamamızın varsayılan sayfası yapabiliriz, çünkü artık hesap verilerini kalıcı hale getiriyoruz. Herhangi bir veri bulunmazsa, dashboard zaten *Giriş* sayfasına yönlendirme işlemini gerçekleştiriyor. `updateRoute()` içinde, geri dönüş `return navigate('/login');`satırını`return navigate('/dashboard');` ile değiştirin.
Artık hesap verilerini kalıcı hale getirdiğimiz için *Dashboard* sayfasını uygulamamızın varsayılan sayfası yapabiliriz. Eğer veri bulunmazsa, dashboard zaten *Giriş* sayfasına yönlendirme işlemini gerçekleştiriyor. `updateRoute()` içinde, geri dönüş `return navigate('/login');`ifadesini`return navigate('/dashboard');` ile değiştirin.
Şimdi uygulamada oturum açın ve sayfayı yenilemeyi deneyin. Dashboard'da kalmalısınız. Bu güncelleme ile tüm başlangıç sorunlarımızı ele aldık...
## Verileri yenileme
## Verileri Yenilemek
...Ama aynı zamanda yeni bir sorun yaratmış olabiliriz. Oops!
`test` hesabını kullanarak dashboard'a gidin, ardından bir terminalde yeni bir işlem oluşturmak için şu komutu çalıştırın:
`test` hesabını kullanarak dashboard'a gidin, ardından bir terminalde şu komutu çalıştırarak yeni bir işlem oluşturun:
```sh
curl --request POST \
@ -223,13 +223,13 @@ curl --request POST \
Şimdi tarayıcıdaki dashboard sayfasını yenilemeyi deneyin. Ne oluyor? Yeni işlemi görüyor musunuz?
Durum,`localStorage` sayesinde süresiz olarak kalıcı hale geliyor, ancak bu aynı zamanda, uygulamadan çıkış yapıp tekrar giriş yapana kadar hiç güncellenmediği anlamına geliyor!
Durum `localStorage` sayesinde süresiz olarak kalıcı hale geldi, ancak bu aynı zamanda uygulamadan çıkış yapıp tekrar giriş yapana kadar hiç güncellenmediği anlamına geliyor!
Bunu düzeltmek için olası bir strateji, dashboard her yüklendiğinde hesap verilerini yeniden yüklemek olabilir, böylece eski verilerden kaçınılır.
### Görev
Yeni bir `updateAccountData`işlevi oluşturun:
Yeni bir `updateAccountData`fonksiyonu oluşturun:
```js
async function updateAccountData() {
@ -249,7 +249,7 @@ async function updateAccountData() {
Bu yöntem, şu anda oturum açmış olduğumuzu kontrol eder ve ardından hesap verilerini sunucudan yeniden yükler.
`refresh` adında başka bir işlev oluşturun:
`refresh` adında başka bir fonksiyon oluşturun:
```js
async function refresh() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
Bu işlev hesap verilerini günceller ve ardından dashboard sayfasının HTML'ini günceller. Dashboard rotası yüklendiğinde çağırmamız gereken şey budur. Rota tanımını şu şekilde güncelleyin:
Bu, hesap verilerini günceller ve ardından dashboard sayfasının HTML'sini günceller. Dashboard rotası yüklendiğinde çağırmamız gereken şey budur. Rota tanımını şu şekilde güncelleyin:
```js
const routes = {
@ -267,28 +267,28 @@ const routes = {
};
```
Şimdi dashboard'u yenilemeyi deneyin, güncellenmiş hesap verilerini göstermesi gerekiyor.
Şimdi dashboard'u yenilemeyi deneyin, güncellenmiş hesap verilerini göstermesi gerekir.
---
## 🚀 Zorluk
Dashboard her yüklendiğinde hesap verilerini yeniden yüklediğimize göre, sizce *tüm hesap* verilerini kalıcı hale getirmemiz hala gerekli mi?
Artık dashboard her yüklendiğinde hesap verilerini yeniden yüklüyoruz, sizce *tüm hesap* verilerini kalıcı hale getirmemiz hala gerekli mi?
Birlikte çalışarak `localStorage` içinde yalnızca uygulamanın çalışması için kesinlikle gerekli olan verileri saklayacak ve yükleyecek şekilde değişiklik yapmayı deneyin.
Birlikte çalışarak `localStorage`'da saklanan ve yüklenen verileri yalnızca uygulamanın çalışması için kesinlikle gerekli olanlarla sınırlamayı deneyin.
## Ders Sonrası Test
[Ders sonrası test](https://ff-quizzes.netlify.app/web/quiz/48)
Ödevi tamamladıktan sonra ortaya çıkan örnek sonuç aşağıda gösterilmiştir:


---
**Feragatname**:
Bu belge, AI çeviri hizmeti [Co-op Translator](https://github.com/Azure/co-op-translator) kullanılarak çevrilmiştir. Doğruluk için çaba göstersek de, otomatik çevirilerin hata veya yanlışlıklar içerebileceğini lütfen unutmayın. Belgenin orijinal dili, yetkili kaynak olarak kabul edilmelidir. Kritik bilgiler için profesyonel insan çevirisi önerilir. Bu çevirinin kullanımından kaynaklanan yanlış anlamalar veya yanlış yorumlamalardan sorumlu değiliz.
Bu belge, AI çeviri hizmeti [Co-op Translator](https://github.com/Azure/co-op-translator) kullanılarak çevrilmiştir. Doğruluk için çaba göstersek de, otomatik çevirilerin hata veya yanlışlıklar içerebileceğini lütfen unutmayın. Belgenin orijinal dili, yetkili kaynak olarak kabul edilmelidir. Kritik bilgiler için profesyonel insan çevirisi önerilir. Bu çevirinin kullanımından kaynaklanan yanlış anlamalar veya yanlış yorumlamalar için sorumluluk kabul etmiyoruz.
Зі зростанням веб-додатку стає складно відстежувати всі потоки даних. Який код отримує дані, яка сторінка їх використовує, де і коли їх потрібно оновлювати... Легко опинитися з заплутаним кодом, який важко підтримувати. Це особливо актуально, коли потрібно ділитися даними між різними сторінками додатку, наприклад, даними користувача. Концепція *управління станом* завжди існувала у всіх типах програм, але з ростом складності веб-додатків це стало ключовим моментом, який варто враховувати під час розробки.
Зі зростанням веб-додатка стає складніше відстежувати всі потоки даних. Який код отримує дані, яка сторінка їх використовує, де і коли їх потрібно оновлювати... Легко опинитися з заплутаним кодом, який важко підтримувати. Це особливо актуально, коли потрібно ділитися даними між різними сторінками додатка, наприклад, даними користувача. Концепція *управління станом* завжди існувала у всіх типах програм, але зі зростанням складності веб-додатків це стало ключовим моментом, який варто враховувати під час розробки.
У цій фінальній частині ми переглянемо додаток, який ми створили, щоб переосмислити, як управляється стан, забезпечуючи підтримку оновлення браузера в будь-який момент і збереження даних між сеансами користувача.
### Передумови
Вам потрібно завершити [отримання даних](../3-data/README.md) у веб-додатку для цього уроку. Також необхідно встановити [Node.js](https://nodejs.org) і [запустити сервер API](../api/README.md) локально, щоб ви могли управляти даними облікового запису.
Вам потрібно завершити частину [отримання даних](../3-data/README.md) веб-додатка для цього уроку. Також необхідно встановити [Node.js](https://nodejs.org) і [запустити сервер API](../api/README.md) локально, щоб ви могли керувати даними облікового запису.
Ви можете перевірити, чи сервер працює належним чином, виконавши цю команду в терміналі:
@ -34,30 +34,30 @@ curl http://localhost:5000/api
## Переосмислення управління станом
У [попередньому уроці](../3-data/README.md) ми ввели базову концепцію стану в нашому додатку за допомогою глобальної змінної `account`, яка містить банківські дані для поточного користувача, що увійшов у систему. Однак наша поточна реалізація має деякі недоліки. Спробуйте оновити сторінку, коли ви перебуваєте на інформаційній панелі. Що відбувається?
У [попередньому уроці](../3-data/README.md) ми ввели базове поняття стану в нашому додатку за допомогою глобальної змінної `account`, яка містить банківські дані для поточного користувача. Однак наша поточна реалізація має деякі недоліки. Спробуйте оновити сторінку, коли ви на інформаційній панелі. Що відбувається?
Є три проблеми з поточним кодом:
- Стан не зберігається, оскільки оновлення браузера повертає вас на сторінку входу.
- Є кілька функцій, які змінюють стан. Зі зростанням додатку це може ускладнити відстеження змін, і легко забути оновити одну з них.
- Стан не очищається, тому коли ви натискаєте *Вийти*, дані облікового запису все ще залишаються, навіть якщо ви перебуваєте на сторінці входу.
- Є кілька функцій, які змінюють стан. У міру зростання додатка це може ускладнити відстеження змін, і легко забути оновити одну з них.
- Стан не очищається, тому коли ви натискаєте *Вийти*, дані облікового запису все ще залишаються, навіть якщо ви на сторінці входу.
Ми могли б оновити наш код, щоб вирішити ці проблеми по одному, але це створило б більше дублювання коду і зробило додаток більш складним і важким для підтримки. Або ми могли б зупинитися на кілька хвилин і переосмислити нашу стратегію.
Ми могли б оновити наш код, щоб вирішити ці проблеми по одному, але це створило б більше дублювання коду і зробило б додаток складнішим для підтримки. Або ми могли б зупинитися на кілька хвилин і переосмислити нашу стратегію.
> Які проблеми ми насправді намагаємося вирішити?
[Управління станом](https://en.wikipedia.org/wiki/State_management) полягає у знаходженні хорошого підходу для вирішення цих двох конкретних проблем:
[Управління станом](https://uk.wikipedia.org/wiki/Управління_станом) полягає у знаходженні хорошого підходу для вирішення цих двох конкретних проблем:
- Як зробити потоки даних у додатку зрозумілими?
- Як забезпечити синхронізацію даних стану з інтерфейсом користувача (і навпаки)?
- Як забезпечити, щоб дані стану завжди були синхронізовані з інтерфейсом користувача (і навпаки)?
Як тільки ви вирішите ці проблеми, будь-які інші проблеми, які можуть виникнути, або вже будуть вирішені, або стануть легшими для вирішення. Існує багато можливих підходів для вирішення цих проблем, але ми оберемо загальне рішення, яке полягає у**централізації даних і способів їх зміни**. Потоки даних виглядатимуть так:
Як тільки ви вирішите ці питання, будь-які інші проблеми, які можуть виникнути, або вже будуть вирішені, або стануть легшими для вирішення. Є багато можливих підходів для вирішення цих проблем, але ми оберемо поширене рішення, яке полягає у**централізації даних і способів їх зміни**. Потоки даних виглядатимуть так:


> Тут ми не будемо розглядати частину, де дані автоматично викликають оновлення представлення, оскільки це пов'язано з більш складними концепціями [Реактивного програмування](https://en.wikipedia.org/wiki/Reactive_programming). Це хороший предмет для подальшого вивчення, якщо ви готові до глибокого занурення.
> Ми не будемо тут розглядати частину, де дані автоматично оновлюють вигляд, оскільки це пов'язано з більш складними концепціями [Реактивного програмування](https://uk.wikipedia.org/wiki/Реактивне_програмування). Це гарна тема для подальшого вивчення, якщо ви готові до глибшого занурення.
✅ Існує багато бібліотек з різними підходами до управління станом, [Redux](https://redux.js.org) є популярним варіантом. Ознайомтеся з концепціями і шаблонами, які використовуються, оскільки це часто хороший спосіб дізнатися, з якими потенційними проблемами ви можете зіткнутися у великих веб-додатках і як їх можна вирішити.
✅ Існує багато бібліотек з різними підходами до управління станом, [Redux](https://redux.js.org) є популярним варіантом. Ознайомтеся з концепціями та шаблонами, які використовуються, оскільки це часто хороший спосіб дізнатися, з якими потенційними проблемами ви можете зіткнутися у великих веб-додатках і як їх можна вирішити.
### Завдання
@ -75,7 +75,7 @@ let state = {
};
```
Ідея полягає у*централізації* всіх даних нашого додатку в одному об'єкті стану. Зараз у стані є лише `account`, тому це не сильно змінює ситуацію, але створює основу для подальших змін.
Ідея полягає в тому, щоб *централізувати* всі дані нашого додатка в одному об'єкті стану. Зараз у нас є лише `account`у стані, тому це не змінює багато, але створює основу для подальших змін.
Також потрібно оновити функції, які його використовують. У функціях `register()`і`login()` замініть `account = ...` на `state.account = ...`;
@ -85,15 +85,15 @@ let state = {
const account = state.account;
```
Цей рефакторинг сам по собі не приніс значних покращень, але ідея полягала у створенні основи для наступних змін.
Цей рефакторинг сам по собі не приніс великих покращень, але ідея полягала в тому, щоб закласти основу для наступних змін.
## Відстеження змін даних
Тепер, коли ми створили об'єкт `state` для зберігання наших даних, наступним кроком є централізація оновлень. Мета полягає у тому, щоб зробити легшим відстеження будь-яких змін і моментів, коли вони відбуваються.
Тепер, коли ми створили об'єкт `state` для зберігання наших даних, наступним кроком є централізація оновлень. Мета полягає в тому, щоб полегшити відстеження будь-яких змін і моментів, коли вони відбуваються.
Щоб уникнути змін уоб'єкті `state`, також хорошою практикою є вважати його [*незмінним*](https://en.wikipedia.org/wiki/Immutable_object), тобто таким, який взагалі не можна змінювати. Це також означає, що вам потрібно створити новий об'єкт стану, якщо ви хочете щось у ньому змінити. Роблячи це, ви створюєте захист від потенційно небажаних [побічних ефектів](https://en.wikipedia.org/wiki/Side_effect_(computer_science)),і відкриваєте можливості для нових функцій у вашому додатку, таких як реалізація undo/redo, а також спрощуєте налагодження. Наприклад, ви могли б реєструвати кожну зміну стану і зберігати історію змін, щоб зрозуміти джерело помилки.
Щоб уникнути змін об'єкта `state`, також є гарною практикою вважати його [*незмінним*](https://uk.wikipedia.org/wiki/Незмінний_об'єкт), тобто таким, що не може бути змінений взагалі. Це також означає, що вам потрібно створити новий об'єкт стану, якщо ви хочете щось у ньому змінити. Таким чином, ви захищаєтеся від потенційно небажаних [побічних ефектів](https://uk.wikipedia.org/wiki/Побічний_ефект_(інформатика))і відкриваєте можливості для нових функцій у вашому додатку, таких як реалізація скасування/повтору, а також спрощуєте налагодження. Наприклад, ви могли б реєструвати кожну зміну стану і зберігати історію змін, щоб зрозуміти джерело помилки.
У JavaScript ви можете використовувати [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) для створення незмінної версіїоб'єкта. Якщо ви спробуєте внести зміни до незмінного об'єкта, буде викликано виняток.
У JavaScript ви можете використовувати [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze), щоб створити незмінну версіюоб'єкта. Якщо ви спробуєте внести зміни до незмінного об'єкта, буде викликано виняток.
✅ Чи знаєте ви різницю між *поверхневим*і*глибоким* незмінним об'єктом? Ви можете прочитати про це [тут](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
У цій функції ми створюємо новий об'єкт стану і копіюємо дані з попереднього стану за допомогою [*оператора розгортання (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Потім ми перевизначаємо конкретну властивість об'єкта стану новими даними, використовуючи [нотацію квадратних дужок](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` для присвоєння. Нарешті, ми блокуємо об'єкт, щоб запобігти модифікаціям, використовуючи `Object.freeze()`. Зараз устані зберігається лише властивість `account`, але з таким підходом ви можете додати стільки властивостей, скільки вам потрібно.
У цій функції ми створюємо новий об'єкт стану і копіюємо дані з попереднього стану за допомогою [*оператора розпакування (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Потім ми перевизначаємо певну властивість об'єкта стану новими даними, використовуючи [нотацію з квадратними дужками](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` для присвоєння. Нарешті, ми блокуємо об'єкт, щоб запобігти модифікаціям, використовуючи `Object.freeze()`. Зараз унас є лише властивість `account`, збережена в стані, але з цим підходом ви можете додати стільки властивостей, скільки потрібно.
Також оновимо ініціалізацію `state`, щоб переконатися, що початковий стан також заморожений:
Ми також оновимо ініціалізацію `state`, щоб переконатися, що початковий стан також заморожений:
```js
let state = Object.freeze({
@ -126,13 +126,13 @@ let state = Object.freeze({
updateState('account', result);
```
Зробіть те ж саме з функцією `login`, замінивши `state.account = data;` на:
Те ж саме зробіть із функцією `login`, замінивши `state.account = data;` на:
```js
updateState('account', data);
```
Тепер скористаємося можливістю вирішити проблему з даними облікового запису, які не очищаються, коли користувач натискає *Вийти*.
Тепер скористаємося можливістю виправити проблему з тим, що дані облікового запису не очищаються, коли користувач натискає *Вийти*.
Створіть нову функцію `logout()`:
@ -145,47 +145,47 @@ function logout() {
У`updateDashboard()` замініть перенаправлення `return navigate('/login');` на `return logout();`
Спробуйте зареєструвати новий обліковий запис, вийти і знову увійти, щоб перевірити, чи все працює правильно.
Спробуйте зареєструвати новий обліковий запис, вийти і знову увійти, щоб переконатися, що все працює правильно.
> Порада: ви можете переглянути всі зміни стану, додавши `console.log(state)` в кінці `updateState()`і відкривши консоль у засобах розробки вашого браузера.
> Порада: ви можете переглянути всі зміни стану, додавши `console.log(state)` в кінці `updateState()`і відкривши консоль у засобах розробника вашого браузера.
## Збереження стану
Більшість веб-додатків потребують збереження даних для коректної роботи. Усі критичні дані зазвичай зберігаються в базі даних і доступні через серверний API, як у нашому випадку з даними облікового запису користувача. Але іноді також цікаво зберігати деякі дані в клієнтському додатку, який працює у вашому браузері, для покращення досвіду користувача або підвищення продуктивності завантаження.
Більшість веб-додатків потребують збереження даних для коректної роботи. Усі критичні дані зазвичай зберігаються в базі даних і доступні через серверний API, як у нашому випадку з даними облікового запису користувача. Але іноді також цікаво зберігати деякі дані на клієнтському додатку, який працює у вашому браузері, для кращого користувацького досвіду або для покращення продуктивності завантаження.
Коли ви хочете зберігати дані у вашому браузері, є кілька важливих питань, які варто задатисобі:
Коли ви хочете зберігати дані у вашому браузері, є кілька важливих питань, які варто задати:
- *Чи є дані конфіденційними?* Ви повинні уникати зберігання будь-яких конфіденційних даних на клієнті, таких як паролі користувачів.
- *Як довго вам потрібно зберігати ці дані?* Ви плануєте отримувати доступ до цих даних лише протягом поточного сеансу чи хочете, щоб вони зберігалися назавжди?
- *Як довго вам потрібно зберігати ці дані?* Ви плануєте отримувати доступ до цих даних лише під час поточного сеансу чи хочете, щоб вони зберігалися назавжди?
Існує кілька способів зберігання інформації всередині веб-додатку, залежно від того, що ви хочете досягти. Наприклад, ви можете використовувати URL-адреси для зберігання пошукового запиту і зробити його доступним для спільного використання між користувачами. Ви також можете використовувати [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), якщо дані потрібно передавати на сервер, наприклад, інформацію для [автентифікації](https://en.wikipedia.org/wiki/Authentication).
Існує кілька способів зберігання інформації у веб-додатку, залежно від того, чого ви хочете досягти. Наприклад, ви можете використовувати URL-адреси для зберігання пошукового запиту і зробити його доступним для інших користувачів. Ви також можете використовувати [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies), якщо дані потрібно передавати на сервер, наприклад, інформацію для [автентифікації](https://uk.wikipedia.org/wiki/Автентифікація).
Ще один варіант — використовувати один із багатьох API браузера для зберігання даних. Два з них особливо цікаві:
Інший варіант — використовувати один із багатьох API браузера для зберігання даних. Два з них є особливо цікавими:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Сховище ключ/значення](https://en.wikipedia.org/wiki/Key%E2%80%93value_database), яке дозволяє зберігати дані, специфічні для поточного веб-сайту, між різними сеансами. Дані, збережені в ньому, ніколи не закінчуються.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): працює так само, як `localStorage`, за винятком того, що дані, збережені в ньому, очищуються після завершення сеансу (коли браузер закривається).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Сховище ключ/значення](https://uk.wikipedia.org/wiki/База_даних_ключ-значення), яке дозволяє зберігати дані, специфічні для поточного веб-сайту, між різними сеансами. Збережені дані ніколи не закінчуються.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): працює так само, як `localStorage`, за винятком того, що дані, збережені в ньому, очищаються після завершення сеансу (коли браузер закривається).
Зверніть увагу, що обидва ці API дозволяють зберігати лише [рядки](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Якщо ви хочете зберігати складні об'єкти, вам потрібно буде серіалізувати їх у формат [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) за допомогою [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Якщо ви хочете створити веб-додаток, який не працює з сервером, також можливо створити базу даних на клієнті, використовуючи API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Цей варіант зарезервований для складних випадків використання або якщо вам потрібно зберігати значну кількість даних, оскільки він складніший у використанні.
✅ Якщо ви хочете створити веб-додаток, який не працює з сервером, також можливо створити базу даних на клієнті, використовуючи [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Цей варіант зарезервований для складних випадків використання або якщо вам потрібно зберігати значну кількість даних, оскільки він складніший у використанні.
### Завдання
Ми хочемо, щоб наші користувачі залишалися в системі, поки вони явно не натиснуть кнопку *Вийти*, тому ми будемо використовувати `localStorage` для зберігання даних облікового запису. Спочатку визначимо ключ, який будемо використовувати для зберігання наших даних.
Ми хочемо, щоб наші користувачі залишалися в системі, поки вони явно не натиснуть кнопку *Вийти*, тому ми будемо використовувати `localStorage` для зберігання даних облікового запису. Спочатку визначимо ключ, який ми будемо використовувати для зберігання наших даних.
```js
const storageKey = 'savedAccount';
```
Потім додайте цей рядок у кінці функції `updateState()`:
Потім додайте цей рядок у кінець функції `updateState()`:
Завдяки цьому дані облікового запису користувача будуть збережені і завжди актуальні, оскільки ми централізували всі оновлення стану раніше. Тут ми починаємо отримувати вигоду від усіх наших попередніх рефакторингів 🙂.
Таким чином, дані облікового запису користувача будуть збережені і завжди актуальні, оскільки ми централізували всі оновлення стану. Це той момент, коли ми починаємо отримувати вигоду від усіх наших попередніх рефакторингів 🙂.
Оскільки дані зберігаються, ми також повинні подбати про їх відновлення, коли додаток завантажується. Оскільки ми почнемо мати більше коду для ініціалізації, можливо, буде хорошою ідеєю створити нову функцію `init`, яка також включає наш попередній код у кінці `app.js`:
Оскільки дані зберігаються, ми також повинні подбати про їх відновлення під час завантаження додатка. Оскільки у нас починає з'являтися більше коду ініціалізації, можливо, варто створити нову функцію `init`, яка також включає наш попередній код у кінці `app.js`:
```js
function init() {
@ -202,17 +202,17 @@ function init() {
init();
```
Тут ми отримуємо збережені дані, і якщо вони є, ми оновлюємо стан відповідно. Важливо зробити це *перед* оновленням маршруту, оскільки може бути код, який залежить від стану під час оновлення сторінки.
Тут ми отримуємо збережені дані, і якщо вони є, оновлюємо стан відповідно. Важливо зробити це *до* оновлення маршруту, оскільки під час оновлення сторінки може бути код, який залежить від стану.
Ми також можемо зробити сторінку *Dashboard* сторінкою за замовчуванням нашого додатку, оскільки тепер ми зберігаємо дані облікового запису. Якщо дані не знайдені, інформаційна панель все одно перенаправляє на сторінку *Login*. У`updateRoute()` замініть резервний варіант `return navigate('/login');` на `return navigate('/dashboard');`.
Ми також можемо зробити сторінку *Dashboard* сторінкою за замовчуванням нашого додатка, оскільки тепер ми зберігаємо дані облікового запису. Якщо дані не знайдені, інформаційна панель все одно перенаправляє на сторінку *Login*. У`updateRoute()` замініть резервний варіант `return navigate('/login');` на `return navigate('/dashboard');`.
Тепер увійдіть у додаток і спробуйте оновити сторінку. Ви повинні залишитися на інформаційній панелі. Завдяки цьому оновленню ми вирішили всі наші початкові проблеми...
Тепер увійдіть у додаток і спробуйте оновити сторінку. Ви повинні залишитися на інформаційній панелі. З цим оновленням ми вирішили всі наші початкові проблеми...
## Оновлення даних
...Але ми також могли створити нову проблему. Ой!
...Але ми могли також створити нову проблему. Ой!
Перейдіть на інформаційну панель, використовуючи обліковий запис `test`, апотім виконайте цю команду в терміналі, щоб створити нову транзакцію:
Перейдіть на інформаційну панель, використовуючи обліковий запис `test`, потім виконайте цю команду в терміналі, щоб створити нову транзакцію:
Тепер спробуйте оновити сторінку інформаційної панелі у браузері. Що відбувається? Ви бачите нову транзакцію?
Тепер спробуйте оновити сторінку інформаційної панелі у браузері. Що відбувається? Чи бачите ви нову транзакцію?
Стан зберігається безстроково завдяки `localStorage`, але це також означає, що він ніколи не оновлюється, поки ви не вийдете з додаткуі знову не увійдете!
Стан зберігається на невизначений час завдяки `localStorage`, але це також означає, що він ніколи не оновлюється, поки ви не вийдете з додаткаі не ввійдете знову!
Одна з можливих стратегій вирішення цієї проблеми — перезавантажувати дані облікового запису кожного разу, коли завантажується інформаційна панель, щоб уникнути застарілих даних.
Однією з можливих стратегій вирішення цієї проблеми є перезавантаження даних облікового запису щоразу, коли завантажується інформаційна панель, щоб уникнути застарілих даних.
### Завдання
@ -249,7 +249,7 @@ async function updateAccountData() {
Цей метод перевіряє, чи ви зараз увійшли в систему, а потім перезавантажує дані облікового запису з сервера.
Створіть ще одну функцію під назвою `refresh`:
Створіть іншу функцію під назвою `refresh`:
```js
async function refresh() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
Ця функція оновлює дані облікового запису, а потім займається оновленням HTML сторінки інформаційної панелі. Це те, що нам потрібно викликати, коли завантажується маршрут інформаційної панелі. Оновіть визначення маршруту:
Ця функція оновлює дані облікового запису, а потім оновлює HTML сторінки інформаційної панелі. Це те, що нам потрібно викликати, коли завантажується маршрут інформаційної панелі. Оновіть визначення маршруту:
```js
const routes = {
@ -273,10 +273,12 @@ const routes = {
## 🚀 Виклик
Тепер, коли ми перезавантажуємо дані облікового запису кожного разу, коли завантажується інформаційна панель, як ви думаєте, чи потрібно нам все ще зберігати *всі дані облікового запису*?
Тепер, коли ми перезавантажуємо дані облікового запису щоразу, коли завантажується інформаційна панель, як ви думаєте, чи потрібно нам все ще з
[Тест після лекції](https://ff-quizzes.netlify.app/web/quiz/48)
Цей документ було перекладено за допомогою сервісу автоматичного перекладу [Co-op Translator](https://github.com/Azure/co-op-translator). Хоча ми прагнемо до точності, зверніть увагу, що автоматичні переклади можуть містити помилки або неточності. Оригінальний документ мовою оригіналу слід вважати авторитетним джерелом. Для критично важливої інформації рекомендується професійний переклад людиною. Ми не несемо відповідальності за будь-які непорозуміння або неправильні тлумачення, що виникли внаслідок використання цього перекладу.
Цей документ було перекладено за допомогою сервісу автоматичного перекладу [Co-op Translator](https://github.com/Azure/co-op-translator). Хоча ми прагнемо до точності, будь ласка, зверніть увагу, що автоматичні переклади можуть містити помилки або неточності. Оригінальний документ на його рідній мові слід вважати авторитетним джерелом. Для критичної інформації рекомендується професійний людський переклад. Ми не несемо відповідальності за будь-які непорозуміння або неправильні тлумачення, що виникають внаслідок використання цього перекладу.
جب ایک ویب ایپلیکیشن بڑھتی ہے، تو تمام ڈیٹا کے بہاؤ کو ٹریک کرنا ایک چیلنج بن جاتا ہے۔ کون سا کوڈ ڈیٹا حاصل کرتا ہے، کون سا صفحہ اسے استعمال کرتا ہے، کہاں اور کب اسے اپ ڈیٹ کرنے کی ضرورت ہے... یہ آسانی سے پیچیدہ کوڈ میں بدل سکتا ہے جسے برقرار رکھنا مشکل ہو۔ یہ خاص طور پر اس وقت سچ ہے جب آپ کو اپنی ایپ کے مختلف صفحات کے درمیان ڈیٹا شیئر کرنے کی ضرورت ہو، جیسے کہ صارف کا ڈیٹا۔ *اسٹیٹ مینجمنٹ* کا تصور ہمیشہ سے ہر قسم کے پروگراموں میں موجود رہا ہے، لیکن جیسے جیسے ویب ایپس کی پیچیدگی بڑھتی جا رہی ہے، یہ ترقی کے دوران ایک اہم نکتہ بن گیا ہے۔
جب ایک ویب ایپلیکیشن بڑھتی ہے، تو تمام ڈیٹا فلو کو ٹریک کرنا ایک چیلنج بن جاتا ہے۔ کون سا کوڈ ڈیٹا حاصل کرتا ہے، کون سا صفحہ اسے استعمال کرتا ہے، کہاں اور کب اسے اپ ڈیٹ کرنے کی ضرورت ہے... آسانی سے ایسا کوڈ بن سکتا ہے جو برقرار رکھنا مشکل ہو۔ یہ خاص طور پر اس وقت ہوتا ہے جب آپ کو اپنی ایپ کے مختلف صفحات کے درمیان ڈیٹا شیئر کرنے کی ضرورت ہوتی ہے، جیسے صارف کا ڈیٹا۔ *اسٹیٹ مینجمنٹ* کا تصور ہمیشہ سے ہر قسم کے پروگراموں میں موجود رہا ہے، لیکن جیسے جیسے ویب ایپس پیچیدہ ہوتی جا رہی ہیں، یہ ترقی کے دوران سوچنے کا ایک اہم نقطہ بن گیا ہے۔
اس آخری حصے میں، ہم اس ایپ پر نظر ڈالیں گے جو ہم نے بنائی ہے تاکہ اسٹیٹ کو منظم کرنے کے طریقے پر دوبارہ غور کیا جا سکے، براؤزر ریفریش کو کسی بھی وقت سپورٹ کرنے اور صارف کے سیشنز کے دوران ڈیٹا کو برقرار رکھنے کی اجازت دی جا سکے۔
اس آخری حصے میں، ہم اس ایپ پر نظر ڈالیں گے جو ہم نے بنائی ہے تاکہ اسٹیٹ کو دوبارہ سوچا جا سکے، براؤزر ریفریش کو کسی بھی وقت سپورٹ کرنے کی اجازت دی جا سکے، اور صارف کے سیشنز کے دوران ڈیٹا کو برقرار رکھا جا سکے۔
### پیشگی شرائط
### پیشگی شرط
آپ کو اس سبق کے لیے ویب ایپ کے [ڈیٹا فچنگ](../3-data/README.md) والے حصے کو مکمل کرنا ہوگا۔ آپ کو [Node.js](https://nodejs.org) انسٹال کرنے اور [سرور API](../api/README.md) کو مقامی طور پر چلانے کی بھی ضرورت ہوگی تاکہ آپ اکاؤنٹ کے ڈیٹا کو منظم کر سکیں۔
آپ کو اس سبق کے لیے ویب ایپ کے [ڈیٹا فچنگ](../3-data/README.md) حصے کو مکمل کرنا ہوگا۔ آپ کو [Node.js](https://nodejs.org) انسٹال کرنے اور [سرور API](../api/README.md) کو مقامی طور پر چلانے کی بھی ضرورت ہے تاکہ آپ اکاؤنٹ ڈیٹا کو منظم کر سکیں۔
آپ یہ کمانڈ ٹرمینل میں چلا کر یہ جانچ سکتے ہیں کہ سرور صحیح طریقے سے چل رہا ہے یا نہیں:
آپ یہ کمانڈ ٹرمینل میں چلا کر چیک کر سکتے ہیں کہ سرور صحیح طریقے سے چل رہا ہے:
```sh
curl http://localhost:5000/api
@ -32,36 +32,36 @@ curl http://localhost:5000/api
---
## اسٹیٹ مینجمنٹ پر دوبارہ غور کریں
## اسٹیٹ مینجمنٹ کو دوبارہ سوچیں
پچھلے سبق میں، ہم نے اپنی ایپ میں اسٹیٹ کے ایک بنیادی تصور کو متعارف کرایا تھا، جس میں عالمی `account` ویری ایبل شامل تھا جو موجودہ لاگ ان صارف کے بینک ڈیٹا کو رکھتا ہے۔ تاہم، ہمارے موجودہ نفاذ میں کچھ خامیاں ہیں۔ ڈیش بورڈ پر ہونے کے دوران صفحہ کو ریفریش کرنے کی کوشش کریں۔ کیا ہوتا ہے؟
[پچھلے سبق](../3-data/README.md) میں، ہم نے اپنی ایپ میں اسٹیٹ کے بنیادی تصور کو متعارف کرایا تھا جس میں عالمی `account` ویریبل شامل تھا جو موجودہ لاگ ان صارف کے بینک ڈیٹا کو رکھتا ہے۔ تاہم، ہماری موجودہ عمل درآمد میں کچھ خامیاں ہیں۔ ڈیش بورڈ پر ہونے کے دوران صفحہ کو ریفریش کرنے کی کوشش کریں۔ کیا ہوتا ہے؟
موجودہ کوڈ میں 3 مسائل ہیں:
- اسٹیٹ محفوظ نہیں ہے، کیونکہ براؤزر ریفریش آپ کو لاگ ان صفحے پر واپس لے جاتا ہے۔
- اسٹیٹ کو تبدیل کرنے کے لیے متعدد فنکشنز موجود ہیں۔ جیسے جیسے ایپ بڑھتی ہے، تبدیلیوں کو ٹریک کرنا مشکل ہو سکتا ہے اور کسی ایک کو اپ ڈیٹ کرنا بھول جانا آسان ہو جاتا ہے۔
- اسٹیٹ صاف نہیں کی جاتی، لہذا جب آپ *Logout* پر کلک کرتے ہیں تو اکاؤنٹ کا ڈیٹا اب بھی موجود ہوتا ہے حالانکہ آپ لاگ ان صفحے پر ہیں۔
- اسٹیٹ برقرار نہیں رہتا، کیونکہ براؤزر ریفریش آپ کو لاگ ان صفحے پر واپس لے جاتا ہے۔
- اسٹیٹ کو تبدیل کرنے والے متعدد فنکشنز ہیں۔ جیسے جیسے ایپ بڑھتی ہے، اس سے تبدیلیوں کو ٹریک کرنا مشکل ہو سکتا ہے اور ایک کو اپ ڈیٹ کرنا بھولنا آسان ہو جاتا ہے۔
- اسٹیٹ صاف نہیں ہوتا، لہذا جب آپ *Logout* پر کلک کرتے ہیں تو اکاؤنٹ کا ڈیٹا اب بھی موجود ہوتا ہے حالانکہ آپ لاگ ان صفحے پر ہیں۔
ہم اپنے کوڈ کو ان مسائل کو ایک ایک کرکے حل کرنے کے لیے اپ ڈیٹ کر سکتے ہیں، لیکن اس سے کوڈ کی نقل میں اضافہ ہوگا اور ایپ کو مزید پیچیدہ اور برقرار رکھنے میں مشکل بنا دے گا۔ یا ہم چند منٹ کے لیے رک سکتے ہیں اور اپنی حکمت عملی پر دوبارہ غور کر سکتے ہیں۔
ہم اپنے کوڈ کو ان مسائل کو ایک ایک کرکے حل کرنے کے لیے اپ ڈیٹ کر سکتے ہیں، لیکن اس سے کوڈ کی نقل پیدا ہوگی اور ایپ کو زیادہ پیچیدہ اور برقرار رکھنے میں مشکل ہو جائے گی۔ یا ہم چند منٹ کے لیے رک سکتے ہیں اور اپنی حکمت عملی پر دوبارہ غور کر سکتے ہیں۔
> ہم یہاں اصل میں کون سے مسائل حل کرنے کی کوشش کر رہے ہیں؟
> ہم یہاں واقعی کون سے مسائل حل کرنے کی کوشش کر رہے ہیں؟
[اسٹیٹ مینجمنٹ](https://en.wikipedia.org/wiki/State_management) کا مقصد ان دو خاص مسائل کو حل کرنے کے لیے ایک اچھا طریقہ تلاش کرنا ہے:
[اسٹیٹ مینجمنٹ](https://en.wikipedia.org/wiki/State_management) ان دو خاص مسائل کو حل کرنے کے لیے ایک اچھا طریقہ تلاش کرنے کے بارے میں ہے:
- ایپ میں ڈیٹا کے بہاؤ کو سمجھنے کے قابل کیسے رکھا جائے؟
- ایپ میں ڈیٹا فلو کو سمجھنے کے قابل کیسے رکھا جائے؟
- اسٹیٹ ڈیٹا کو ہمیشہ صارف کے انٹرفیس کے ساتھ (اور اس کے برعکس) ہم آہنگ کیسے رکھا جائے؟
ایک بار جب آپ ان کا خیال رکھ لیں، تو آپ کے پاس موجود کوئی بھی دیگر مسائل یا تو پہلے ہی حل ہو چکے ہوں گے یا انہیں حل کرنا آسان ہو جائے گا۔ ان مسائل کو حل کرنے کے لیے بہت سے ممکنہ طریقے ہیں، لیکن ہم ایک عام حل کے ساتھ جائیں گے جو **ڈیٹا اور اسے تبدیل کرنے کے طریقوں کو مرکزی بنانے** پر مشتمل ہے۔ ڈیٹا کے بہاؤ اس طرح جائیں گے:
ایک بار جب آپ ان کا خیال رکھ لیں، تو آپ کے پاس موجود کوئی بھی دیگر مسائل یا تو پہلے ہی حل ہو چکے ہوں گے یا انہیں حل کرنا آسان ہو گیا ہوگا۔ ان مسائل کو حل کرنے کے لیے بہت سے ممکنہ طریقے ہیں، لیکن ہم ایک عام حل کے ساتھ جائیں گے جس میں **ڈیٹا اور اسے تبدیل کرنے کے طریقے کو مرکزی بنانا** شامل ہے۔ ڈیٹا فلو اس طرح ہوگا:


> ہم یہاں اس حصے کا احاطہ نہیں کریں گے جہاں ڈیٹا خود بخود ویو اپ ڈیٹ کو متحرک کرتا ہے، کیونکہ یہ [ری ایکٹو پروگرامنگ](https://en.wikipedia.org/wiki/Reactive_programming) کے مزید جدید تصورات سے جڑا ہوا ہے۔ اگر آپ گہرائی میں جانا چاہتے ہیں تو یہ ایک اچھا فالو اپ موضوع ہے۔
> ہم یہاں اس حصے کا احاطہ نہیں کریں گے جہاں ڈیٹا خود بخود ویو اپ ڈیٹ کو متحرک کرتا ہے، کیونکہ یہ [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming) کے زیادہ جدید تصورات سے جڑا ہوا ہے۔ اگر آپ گہرائی میں جانے کے لیے تیار ہیں تو یہ ایک اچھا فالو اپ موضوع ہے۔
✅ اسٹیٹ مینجمنٹ کے مختلف طریقوں کے ساتھ بہت سی لائبریریاں موجود ہیں، [Redux](https://redux.js.org) ایک مقبول آپشن ہے۔ ان تصورات اور پیٹرنز پر ایک نظر ڈالیں کیونکہ یہ اکثر یہ سیکھنے کا ایک اچھا طریقہ ہوتا ہے کہ آپ کو بڑی ویب ایپس میں کن ممکنہ مسائل کا سامنا ہو سکتا ہے اور انہیں کیسے حل کیا جا سکتا ہے۔
✅ اسٹیٹ مینجمنٹ کے مختلف طریقوں کے ساتھ بہت سی لائبریریاں موجود ہیں، [Redux](https://redux.js.org) ایک مقبول آپشن ہے۔ استعمال شدہ تصورات اور پیٹرنز پر ایک نظر ڈالیں کیونکہ یہ اکثر یہ سیکھنے کا ایک اچھا طریقہ ہوتا ہے کہ آپ کو بڑی ویب ایپس میں کن ممکنہ مسائل کا سامنا ہو سکتا ہے اور اسے کیسے حل کیا جا سکتا ہے۔
### کام
ہم تھوڑا سا ریفیکٹرنگ سے شروع کریں گے۔ `account` کی ڈیکلریشن کو تبدیل کریں:
ہم تھوڑا سا ریفیکٹرنگ کے ساتھ شروع کریں گے۔ `account` ڈیکلریشن کو تبدیل کریں:
```js
let account = null;
@ -75,9 +75,9 @@ let state = {
};
```
خیال یہ ہے کہ ہمارے ایپ کے تمام ڈیٹا کو ایک واحد اسٹیٹ آبجیکٹ میں *مرکزی بنایا جائے*۔ فی الحال ہمارے پاس اسٹیٹ میں صرف `account` ہے، لہذا اس سے زیادہ فرق نہیں پڑتا، لیکن یہ ارتقاء کے لیے ایک راستہ بناتا ہے۔
خیال یہ ہے کہ ہمارے ایپ ڈیٹا کو ایک واحد اسٹیٹ آبجیکٹ میں *مرکزی بنانا* ہے۔ ہمارے پاس فی الحال اسٹیٹ میں صرف `account` ہے لہذا اس سے زیادہ فرق نہیں پڑتا، لیکن یہ ارتقاء کے لیے ایک راستہ بناتا ہے۔
ہمیں ان فنکشنز کو بھی اپ ڈیٹ کرنا ہوگا جو اسے استعمال کرتے ہیں۔`register()` اور `login()` فنکشنز میں، `account = ...` کو `state.account = ...` سے تبدیل کریں؛
ہمیں اسے استعمال کرنے والے فنکشنز کو بھی اپ ڈیٹ کرنا ہوگا۔ `register()` اور `login()` فنکشنز میں، `account = ...` کو `state.account = ...` سے تبدیل کریں؛
`updateDashboard()` فنکشن کے آغاز میں، یہ لائن شامل کریں:
@ -85,17 +85,17 @@ let state = {
const account = state.account;
```
یہ ریفیکٹرنگ بذات خود زیادہ بہتری نہیں لائی، لیکن اس کا مقصد اگلی تبدیلیوں کے لیے بنیاد رکھنا تھا۔
یہ ریفیکٹرنگ بذات خود زیادہ بہتری نہیں لائی، لیکن خیال اگلے تبدیلیوں کے لیے بنیاد رکھنا تھا۔
## ڈیٹا کی تبدیلیوں کو ٹریک کریں
## ڈیٹا تبدیلیوں کو ٹریک کریں
اب جب کہ ہم نے اپنے ڈیٹا کو ذخیرہ کرنے کے لیے `state` آبجیکٹ کو جگہ دی ہے، اگلا قدم اپ ڈیٹس کو مرکزی بنانا ہے۔ مقصد یہ ہے کہ کسی بھی تبدیلی اور ان کے ہونے کے وقت کو ٹریک کرنا آسان بنایا جائے۔
اب جب کہ ہم نے اپنا ڈیٹا اسٹور کرنے کے لیے `state` آبجیکٹ کو جگہ دی ہے، اگلا قدم اپ ڈیٹس کو مرکزی بنانا ہے۔ مقصد یہ ہے کہ کسی بھی تبدیلی اور ان کے ہونے کے وقت کو ٹریک کرنا آسان بنایا جائے۔
`state` آبجیکٹ میں تبدیلیاں کرنے سے بچنے کے لیے، اسے [*ناقابل تغیر*](https://en.wikipedia.org/wiki/Immutable_object) سمجھنا بھی ایک اچھا عمل ہے، یعنی اسے بالکل بھی تبدیل نہیں کیا جا سکتا۔ اس کا مطلب یہ بھی ہے کہ اگر آپ اس میں کچھ تبدیل کرنا چاہتے ہیں تو آپ کو ایک نیا اسٹیٹ آبجیکٹ بنانا ہوگا۔ ایسا کرنے سے، آپ ممکنہ ناپسندیدہ [سائیڈ ایفیکٹس](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) کے بارے میں تحفظ پیدا کرتے ہیں، اور اپنی ایپ میں نئی خصوصیات کے امکانات کھولتے ہیں جیسے کہ انڈو/ریڈو کو نافذ کرنا، جبکہ ڈیبگ کرنا بھی آسان بناتے ہیں۔ مثال کے طور پر، آپ اسٹیٹ میں کی گئی ہر تبدیلی کو لاگ کر سکتے ہیں اور کسی بگ کے ماخذ کو سمجھنے کے لیے تبدیلیوں کی تاریخ رکھ سکتے ہیں۔
`state` آبجیکٹ میں تبدیلیاں کرنے سے بچنے کے لیے، اسے [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) سمجھنا بھی ایک اچھا عمل ہے، یعنی اسے بالکل بھی تبدیل نہیں کیا جا سکتا۔ اس کا مطلب یہ بھی ہے کہ اگر آپ اس میں کچھ بھی تبدیل کرنا چاہتے ہیں تو آپ کو ایک نیا اسٹیٹ آبجیکٹ بنانا ہوگا۔ ایسا کرنے سے، آپ ممکنہ طور پر ناپسندیدہ [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) کے بارے میں تحفظ پیدا کرتے ہیں، اور اپنی ایپ میں نئی خصوصیات کے امکانات کھولتے ہیں جیسے undo/redo کو نافذ کرنا، جبکہ اسے ڈیبگ کرنا بھی آسان بناتے ہیں۔ مثال کے طور پر، آپ اسٹیٹ میں کی گئی ہر تبدیلی کو لاگ کر سکتے ہیں اور کسی بگ کے ماخذ کو سمجھنے کے لیے تبدیلیوں کی تاریخ رکھ سکتے ہیں۔
جاوا اسکرپٹ میں، آپ [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) کا استعمال کر کے کسی آبجیکٹ کا ناقابل تغیر ورژن بنا سکتے ہیں۔ اگر آپ کسی ناقابل تغیر آبجیکٹ میں تبدیلیاں کرنے کی کوشش کرتے ہیں، تو ایک استثناء پیدا ہوگا۔
جاوا اسکرپٹ میں، آپ [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) کا استعمال کر سکتے ہیں تاکہ کسی آبجیکٹ کا ناقابل تبدیل ورژن بنایا جا سکے۔ اگر آپ کسی ناقابل تبدیل آبجیکٹ میں تبدیلیاں کرنے کی کوشش کرتے ہیں، تو ایک استثنا پیدا ہوگا۔
✅ کیا آپ جانتے ہیں کہ *شالو* اور *ڈیپ* ناقابل تغیر آبجیکٹ میں کیا فرق ہے؟ آپ اس کے بارے میں [یہاں](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) پڑھ سکتے ہیں۔
✅ کیا آپ جانتے ہیں کہ *shallow* اور *deep* ناقابل تبدیل آبجیکٹ میں کیا فرق ہے؟ آپ اس کے بارے میں [یہاں](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) پڑھ سکتے ہیں۔
### کام
@ -110,9 +110,9 @@ function updateState(property, newData) {
}
```
اس فنکشن میں، ہم ایک نیا اسٹیٹ آبجیکٹ بنا رہے ہیں اور پچھلے اسٹیٹ سے ڈیٹا کو [*اسپریڈ (`...`) آپریٹر*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) کا استعمال کرتے ہوئے کاپی کر رہے ہیں۔ پھر ہم اسٹیٹ آبجیکٹ کی ایک خاص پراپرٹی کو نئے ڈیٹا کے ساتھ [بریکٹ نوٹیشن](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` کا استعمال کرتے ہوئے اوور رائیڈ کرتے ہیں۔ آخر میں، ہم `Object.freeze()` کا استعمال کرتے ہوئے آبجیکٹ کو لاک کر دیتے ہیں تاکہ ترمیمات کو روکا جا سکے۔ فی الحال ہمارے پاس اسٹیٹ میں صرف `account` پراپرٹی محفوظ ہے، لیکن اس طریقے سے آپ اسٹیٹ میں جتنی چاہیں پراپرٹیز شامل کر سکتے ہیں۔
اس فنکشن میں، ہم ایک نیا اسٹیٹ آبجیکٹ بنا رہے ہیں اور پچھلے اسٹیٹ سے ڈیٹا کو [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) کا استعمال کرتے ہوئے کاپی کر رہے ہیں۔ پھر ہم اسٹیٹ آبجیکٹ کی ایک خاص پراپرٹی کو [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` کے ذریعے نئے ڈیٹا کے ساتھ اوور رائیڈ کرتے ہیں۔ آخر میں، ہم `Object.freeze()` کا استعمال کرتے ہوئے آبجیکٹ کو لاک کرتے ہیں تاکہ ترمیمات کو روکا جا سکے۔ فی الحال ہمارے پاس اسٹیٹ میں صرف `account` پراپرٹی محفوظ ہے، لیکن اس طریقے سے آپ اسٹیٹ میں جتنی پراپرٹیز چاہیں شامل کر سکتے ہیں۔
ہم اس بات کو یقینی بنانے کے لیے `state` کی ابتدائی حالت کو بھی اپ ڈیٹ کریں گے کہ ابتدائی اسٹیٹ بھی منجمد ہو:
ہم اسٹیٹ کی ابتدائی حالت کو بھی اپ ڈیٹ کریں گے تاکہ یہ یقینی بنایا جا سکے کہ ابتدائی اسٹیٹ بھی فریز ہو:
اب ہم موقع کا فائدہ اٹھاتے ہوئے اس مسئلے کو حل کریں گے کہ جب صارف *Logout* پر کلک کرتا ہے تو اکاؤنٹ کا ڈیٹا صاف نہیں ہوتا۔
اب ہم موقع سے فائدہ اٹھاتے ہوئے اس مسئلے کو حل کریں گے کہ جب صارف *Logout* پر کلک کرتا ہے تو اکاؤنٹ کا ڈیٹا صاف نہیں ہوتا۔
ایک نیا فنکشن `logout()` بنائیں:
@ -145,33 +145,33 @@ function logout() {
`updateDashboard()` میں، ری ڈائریکشن `return navigate('/login');` کو `return logout();` سے تبدیل کریں؛
ایک نیا اکاؤنٹ رجسٹر کرنے، لاگ آؤٹ کرنے اور دوبارہ لاگ ان کرنے کی کوشش کریں تاکہ یہ چیک کیا جا سکے کہ سب کچھ اب بھی صحیح طریقے سے کام کر رہا ہے۔
ایک نیا اکاؤنٹ رجسٹر کرنے، لاگ آؤٹ کرنے اور دوبارہ لاگ ان کرنے کی کوشش کریں تاکہ یہ چیک کیا جا سکے کہ سب کچھ صحیح طریقے سے کام کر رہا ہے۔
> ٹپ: آپ `updateState()` کے آخر میں `console.log(state)` شامل کر کے اور اپنے براؤزر کے ڈویلپمنٹ ٹولز میں کنسول کھول کر تمام اسٹیٹ تبدیلیوں پر نظر ڈال سکتے ہیں۔
> ٹپ: آپ براؤزر کے ڈیولپمنٹ ٹولز میں کنسول کھول کر اور `updateState()` کے نیچے `console.log(state)` شامل کر کے تمام اسٹیٹ تبدیلیوں کو دیکھ سکتے ہیں۔
## اسٹیٹ کو برقرار رکھیں
زیادہ تر ویب ایپس کو صحیح طریقے سے کام کرنے کے لیے ڈیٹا کو برقرار رکھنے کی ضرورت ہوتی ہے۔ تمام اہم ڈیٹا عام طور پر ڈیٹا بیس میں محفوظ کیا جاتا ہے اور سرور API کے ذریعے اس تک رسائی حاصل کی جاتی ہے، جیسے کہ ہمارے معاملے میں صارف کے اکاؤنٹ کا ڈیٹا۔ لیکن بعض اوقات، بہتر صارف کے تجربے یا لوڈنگ کی کارکردگی کو بہتر بنانے کے لیے براؤزر میں چلنے والی کلائنٹ ایپ پر کچھ ڈیٹا کو برقرار رکھنا بھی دلچسپ ہوتا ہے۔
زیادہ تر ویب ایپس کو صحیح طریقے سے کام کرنے کے لیے ڈیٹا کو برقرار رکھنے کی ضرورت ہوتی ہے۔ تمام اہم ڈیٹا عام طور پر ڈیٹا بیس میں محفوظ کیا جاتا ہے اور سرور API کے ذریعے رسائی حاصل کی جاتی ہے، جیسے کہ ہمارے معاملے میں صارف اکاؤنٹ ڈیٹا۔ لیکن بعض اوقات، کلائنٹ ایپ میں کچھ ڈیٹا کو برقرار رکھنا بھی دلچسپ ہوتا ہے جو آپ کے براؤزر میں چل رہی ہے، بہتر صارف کے تجربے یا لوڈنگ کی کارکردگی کو بہتر بنانے کے لیے۔
جب آپ اپنے براؤزر میں ڈیٹا کو برقرار رکھنا چاہتے ہیں، تو آپ کو اپنے آپ سے کچھ اہم سوالات پوچھنے چاہئیں:
جب آپ اپنے براؤزر میں ڈیٹا کو برقرار رکھنا چاہتے ہیں، تو آپ کو چند اہم سوالات خود سے پوچھنے چاہئیں:
- *کیا ڈیٹا حساس ہے؟* آپ کو کلائنٹ پر کسی بھی حساس ڈیٹا کو ذخیرہ کرنے سے گریز کرنا چاہیے، جیسے کہ صارف کے پاس ورڈز۔
- *آپ کو یہ ڈیٹا کتنی دیر تک رکھنا ہے؟* کیا آپ اس ڈیٹا تک صرف موجودہ سیشن کے لیے رسائی حاصل کرنے کا ارادہ رکھتے ہیں یا آپ چاہتے ہیں کہ یہ ہمیشہ کے لیے محفوظ رہے؟
- *کیا ڈیٹا حساس ہے؟* آپ کو کلائنٹ پر کوئی حساس ڈیٹا محفوظ کرنے سے گریز کرنا چاہیے، جیسے صارف کے پاس ورڈز۔
- *آپ کو یہ ڈیٹا کتنے عرصے تک رکھنے کی ضرورت ہے؟* کیا آپ اس ڈیٹا تک صرف موجودہ سیشن کے لیے رسائی حاصل کرنے کا ارادہ رکھتے ہیں یا آپ چاہتے ہیں کہ یہ ہمیشہ کے لیے محفوظ رہے؟
ویب ایپ کے اندر معلومات کو ذخیرہ کرنے کے کئی طریقے ہیں، اس پر منحصر ہے کہ آپ کیا حاصل کرنا چاہتے ہیں۔ مثال کے طور پر، آپ تلاش کے استفسار کو ذخیرہ کرنے کے لیے URLs کا استعمال کر سکتے ہیں، اور اسے صارفین کے درمیان شیئر کرنے کے قابل بنا سکتے ہیں۔ اگر ڈیٹا کو سرور کے ساتھ شیئر کرنے کی ضرورت ہو، جیسے کہ [تصدیق](https://en.wikipedia.org/wiki/Authentication) کی معلومات، تو آپ [HTTP کوکیز](https://developer.mozilla.org/docs/Web/HTTP/Cookies) کا استعمال بھی کر سکتے ہیں۔
ویب ایپ کے اندر معلومات کو ذخیرہ کرنے کے کئی طریقے ہیں، اس پر منحصر ہے کہ آپ کیا حاصل کرنا چاہتے ہیں۔ مثال کے طور پر، آپ تلاش کے سوال کو محفوظ کرنے کے لیے URLs کا استعمال کر سکتے ہیں، اور اسے صارفین کے درمیان شیئر کرنے کے قابل بنا سکتے ہیں۔ آپ [HTTP کوکیز](https://developer.mozilla.org/docs/Web/HTTP/Cookies) کا بھی استعمال کر سکتے ہیں اگر ڈیٹا کو سرور کے ساتھ شیئر کرنے کی ضرورت ہو، جیسے [authentication](https://en.wikipedia.org/wiki/Authentication) کی معلومات۔
ایک اور آپشن یہ ہے کہ ڈیٹا کو ذخیرہ کرنے کے لیے براؤزر APIs میں سے ایک کا استعمال کریں۔ ان میں سے دو خاص طور پر دلچسپ ہیں:
ایک اور آپشن یہ ہے کہ ڈیٹا کو اسٹور کرنے کے لیے براؤزر APIs میں سے ایک کا استعمال کریں۔ ان میں سے دو خاص طور پر دلچسپ ہیں:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): ایک [Key/Value اسٹور](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) جو مختلف سیشنز کے دوران موجودہ ویب سائٹ کے لیے مخصوص ڈیٹا کو برقرار رکھنے کی اجازت دیتا ہے۔ اس میں محفوظ کردہ ڈیٹا کبھی ختم نہیں ہوتا۔
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): ایک [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) جو مختلف سیشنز کے دوران موجودہ ویب سائٹ کے لیے مخصوص ڈیٹا کو برقرار رکھنے کی اجازت دیتا ہے۔ اس میں محفوظ کردہ ڈیٹا کبھی ختم نہیں ہوتا۔
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): یہ `localStorage` کی طرح کام کرتا ہے سوائے اس کے کہ اس میں محفوظ کردہ ڈیٹا سیشن ختم ہونے پر (جب براؤزر بند ہو جاتا ہے) صاف ہو جاتا ہے۔
نوٹ کریں کہ یہ دونوں APIs صرف [اسٹرنگز](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) کو ذخیرہ کرنے کی اجازت دیتے ہیں۔ اگر آپ پیچیدہ آبجیکٹس کو ذخیرہ کرنا چاہتے ہیں، تو آپ کو اسے [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) فارمیٹ میں سیریلائز کرنے کی ضرورت ہوگی، [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) کا استعمال کرتے ہوئے۔
نوٹ کریں کہ یہ دونوں APIs صرف [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) کو اسٹور کرنے کی اجازت دیتے ہیں۔ اگر آپ پیچیدہ آبجیکٹس کو اسٹور کرنا چاہتے ہیں، تو آپ کو اسے [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) فارمیٹ میں سیریلائز کرنے کی ضرورت ہوگی [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) کا استعمال کرتے ہوئے۔
✅ اگر آپ ایک ایسی ویب ایپ بنانا چاہتے ہیں جو سرور کے ساتھ کام نہ کرے، تو یہ بھی ممکن ہے کہ [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) کا استعمال کرتے ہوئے کلائنٹ پر ایک ڈیٹا بیس بنایا جائے۔ یہ ایک اعلی درجے کے استعمال کے معاملات کے لیے مخصوص ہے یا اگر آپ کو بڑی مقدار میں ڈیٹا ذخیرہ کرنے کی ضرورت ہو، کیونکہ اسے استعمال کرنا زیادہ پیچیدہ ہے۔
✅ اگر آپ ایک ویب ایپ بنانا چاہتے ہیں جو سرور کے ساتھ کام نہ کرے، تو یہ بھی ممکن ہے کہ [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) کا استعمال کرتے ہوئے کلائنٹ پر ڈیٹا بیس بنایا جائے۔ یہ ایک پیچیدہ استعمال کے معاملات کے لیے محفوظ ہے یا اگر آپ کو ڈیٹا کی نمایاں مقدار کو اسٹور کرنے کی ضرورت ہو، کیونکہ اس کا استعمال زیادہ پیچیدہ ہے۔
### کام
ہم چاہتے ہیں کہ ہمارے صارفین لاگ ان رہیں جب تک کہ وہ واضح طور پر *Logout* بٹن پر کلک نہ کریں، لہذا ہم اکاؤنٹ کے ڈیٹا کو ذخیرہ کرنے کے لیے`localStorage` کا استعمال کریں گے۔ پہلے، آئیے ایک کلید کی وضاحت کریں جسے ہم اپنے ڈیٹا کو ذخیرہ کرنے کے لیے استعمال کریں گے۔
ہم چاہتے ہیں کہ ہمارے صارفین لاگ آؤٹ بٹن پر واضح طور پر کلک کرنے تک لاگ ان رہیں، لہذا ہم`localStorage` کا استعمال کریں گے تاکہ اکاؤنٹ ڈیٹا کو اسٹور کیا جا سکے۔ پہلے، آئیے ایک کلید کی وضاحت کریں جسے ہم اپنے ڈیٹا کو اسٹور کرنے کے لیے استعمال کریں گے۔
اس کے ساتھ، صارف کے اکاؤنٹ کا ڈیٹا محفوظ ہو جائے گا اور ہمیشہ اپ ٹو ڈیٹ رہے گا کیونکہ ہم نے پہلے اپنے تمام اسٹیٹ اپ ڈیٹس کو مرکزی بنایا تھا۔ یہ وہ جگہ ہے جہاں ہم اپنے پچھلے تمام ریفیکٹرز سے فائدہ اٹھانا شروع کرتے ہیں 🙂۔
اس کے ساتھ، صارف اکاؤنٹ ڈیٹا برقرار رہے گا اور ہمیشہ اپ ڈیٹ رہے گا کیونکہ ہم نے پہلے اپنے تمام اسٹیٹ اپ ڈیٹس کو مرکزی بنایا تھا۔ یہ وہ جگہ ہے جہاں ہم اپنے تمام پچھلے ریفیکٹرز سے فائدہ اٹھانا شروع کرتے ہیں 🙂۔
چونکہ ڈیٹا محفوظ ہو گیا ہے، ہمیں ایپ لوڈ ہونے پر اسے بحال کرنے کا بھی خیال رکھنا ہوگا۔ چونکہ اب ہمارے پاس مزید ابتدائی کوڈ ہوگا، یہ ایک نیا `init` فنکشن بنانے کا ایک اچھا خیال ہو سکتا ہے، جس میں `app.js` کے نیچے ہمارا پچھلا کوڈ بھی شامل ہو:
چونکہ ڈیٹا محفوظ ہے، ہمیں اسے ایپ لوڈ ہونے پر بحال کرنے کا بھی خیال رکھنا ہوگا۔ چونکہ ہم زیادہ ابتدائی کوڈ رکھنے لگیں گے، یہ ایک نیا `init` فنکشن بنانے کا اچھا خیال ہو سکتا ہے، جس میں `app.js` کے نیچے ہمارا پچھلا کوڈ بھی شامل ہو:
```js
function init() {
@ -202,9 +202,9 @@ function init() {
init();
```
یہاں ہم محفوظ کردہ ڈیٹا کو بازیافت کرتے ہیں، اور اگر کوئی موجود ہو تو ہم اسٹیٹ کو اسی کے مطابق اپ ڈیٹ کرتے ہیں۔ یہ صفحہ اپ ڈیٹ کے دوران اسٹیٹ پر انحصار کرنے والے کوڈ کے طور پر، راستہ اپ ڈیٹ کرنے سے *پہلے* ایسا کرنا ضروری ہے۔
یہاں ہم محفوظ کردہ ڈیٹا کو بازیافت کرتے ہیں، اور اگر کوئی موجود ہے تو ہم اسٹیٹ کو اسی کے مطابق اپ ڈیٹ کرتے ہیں۔ صفحہ اپ ڈیٹ کے دوران اسٹیٹ پر انحصار کرنے والے کوڈ کے ہونے کے امکان کے پیش نظر، یہ *route* کو اپ ڈیٹ کرنے سے پہلے کرنا ضروری ہے۔
ہم *ڈیش بورڈ* صفحہ کو اپنی ایپلیکیشن کا ڈیفالٹ صفحہ بھی بنا سکتے ہیں، کیونکہ اب ہم اکاؤنٹ کے ڈیٹا کو برقرار رکھ رہے ہیں۔ اگر کوئی ڈیٹا نہیں ملا تو، ڈیش بورڈ لاگ ان صفحے پر ری ڈائریکٹ کرنے کا خیال رکھتا ہے۔ `updateRoute()` میں، فال بیک`return navigate('/login');` کو `return navigate('/dashboard');` سے تبدیل کریں۔
ہم اپنی ایپلیکیشن کا ڈیفالٹ صفحہ *Dashboard*بھی بنا سکتے ہیں، کیونکہ ہم اب اکاؤنٹ ڈیٹا کو برقرار رکھ رہے ہیں۔ اگر کوئی ڈیٹا نہیں ملا، تو ڈیش بورڈ ویسے بھی *Login* صفحے پر ری ڈائریکٹ کرنے کا خیال رکھتا ہے۔ `updateRoute()` میں، fallback`return navigate('/login');` کو `return navigate('/dashboard');` سے تبدیل کریں۔
اب ایپ میں لاگ ان کریں اور صفحہ کو ریفریش کرنے کی کوشش کریں۔ آپ کو ڈیش بورڈ پر رہنا چاہیے۔ اس اپ ڈیٹ کے ساتھ ہم نے اپنے تمام ابتدائی مسائل کا خیال رکھا ہے...
اب براؤزر میں ڈیش بورڈ صفحہ کو ریفریش کرنے کی کوشش کریں۔ کیا ہوتا ہے؟ کیا آپ کو نیا ٹرانزیکشن نظر آتا ہے؟
اب اپنے براؤزر میں ڈیش بورڈ صفحہ کو ریفریش کرنے کی کوشش کریں۔ کیا ہوتا ہے؟ کیا آپ کو نیا ٹرانزیکشن نظر آتا ہے؟
اسٹیٹ کو `localStorage` کی بدولت غیر معینہ مدت تک برقرار رکھا گیا ہے، لیکن اس کا مطلب یہ بھی ہے کہ یہ کبھی اپ ڈیٹ نہیں ہوتا جب تک کہ آپ ایپ سے لاگ آؤٹ نہ کریں اور دوبارہ لاگ ان نہ کریں!
اسٹیٹ `localStorage` کی بدولت غیر معینہ مدت تک برقرار رہتا ہے، لیکن اس کا مطلب یہ بھی ہے کہ یہ کبھی اپ ڈیٹ نہیں ہوتا جب تک کہ آپ ایپ سے لاگ آؤٹ نہ کریں اور دوبارہ لاگ ان نہ کریں!
اسے ٹھیک کرنے کے لیے ایک ممکنہ حکمت عملی یہ ہے کہ ڈیش بورڈ ہر بار لوڈ ہونے پر اکاؤنٹ کے ڈیٹا کو دوبارہ لوڈ کیا جائے، تاکہ ڈیٹا پرانا نہ ہو۔
اسے ٹھیک کرنے کے لیے ایک ممکنہ حکمت عملی یہ ہے کہ ڈیش بورڈ ہر بار لوڈ ہونے پر اکاؤنٹ ڈیٹا کو دوبارہ لوڈ کیا جائے، تاکہ ڈیٹا اسٹال نہ ہو۔
### کام
@ -247,9 +247,9 @@ async function updateAccountData() {
}
```
یہ طریقہ چیک کرتا ہے کہ ہم فی الحال لاگ ان ہیں پھر سرور سے اکاؤنٹ کے ڈیٹا کو دوبارہ لوڈ کرتا ہے۔
یہ طریقہ چیک کرتا ہے کہ ہم فی الحال لاگ ان ہیں پھر سرور سے اکاؤنٹ ڈیٹا کو دوبارہ لوڈ کرتا ہے۔
ایک اور فنکشن `refresh` کے نام سے بنائیں:
ایک اور فنکشن بنائیں جس کا نام `refresh` ہو:
```js
async function refresh() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
یہ اکاؤنٹ کے ڈیٹا کو اپ ڈیٹ کرتا ہے، پھر ڈیش بورڈ صفحے کے HTML کو اپ ڈیٹ کرنے کا خیال رکھتا ہے۔ یہ وہی ہے جسے ہمیں ڈیش بورڈ روٹ لوڈ ہونے پر کال کرنے کی ضرورت ہے۔ روٹ کی تعریف کو اپ ڈیٹ کریں:
یہ اکاؤنٹ ڈیٹا کو اپ ڈیٹ کرتا ہے، پھر ڈیش بورڈ صفحے کے HTML کو اپ ڈیٹ کرنے کا خیال رکھتا ہے۔ یہ وہی ہے جسے ہمیں ڈیش بورڈ روٹ لوڈ ہونے پر کال کرنے کی ضرورت ہے۔ روٹ تعریف کو اپ ڈیٹ کریں:
```js
const routes = {
@ -267,20 +267,28 @@ const routes = {
};
```
اب ڈیش بورڈ کو ریفریش کرنے کی کوشش کریں، یہ اپ ڈیٹ شدہ اکاؤنٹ ڈیٹا کو ظاہر کرنا چاہیے۔
اب ڈیش بورڈ کو دوبارہ لوڈ کرنے کی کوشش کریں، اسے اپ ڈیٹ شدہ اکاؤنٹ ڈیٹا دکھانا چاہیے۔
---
## 🚀 چیلنج
اب جب کہ ہم ہر بار ڈیش بورڈ لوڈ ہونے پر اکاؤنٹ کے ڈیٹا کو دوبارہ لوڈ کرتے ہیں، کیا آپ کو لگتا ہے کہ ہمیں *پورے اکاؤنٹ* کے ڈیٹا کو برقرار رکھنے کی
اب جب کہ ہم ہر بار ڈیش بورڈ لوڈ ہونے پر اکاؤنٹ ڈیٹا کو دوبارہ لوڈ کرتے ہیں، کیا آپ کو لگتا ہے کہ ہمیں *تمام اکاؤنٹ* ڈیٹا کو برقرار رکھنے کی ضرورت ہے؟
یہاں ایک مثال ہے جو اس کام کو مکمل کرنے کے بعد حاصل ہوگی:
مل کر کام کرنے کی کوشش کریں تاکہ `localStorage` سے محفوظ اور لوڈ ہونے والے ڈیٹا کو تبدیل کیا جا سکے تاکہ صرف وہی شامل ہو جو ایپ کے کام کرنے کے لیے بالکل ضروری ہے۔
## لیکچر کے بعد کا کوئز
[لیکچر کے بعد کا کوئز](https://ff-quizzes.netlify.app/web/quiz/48)
## اسائنمنٹ
["Add transaction" ڈائیلاگ کو نافذ کریں](assignment.md)
یہاں اسائنمنٹ مکمل کرنے کے بعد کا ایک مثال نتیجہ ہے:

---
**ڈسکلیمر**:
یہ دستاویز AI ترجمہ سروس [Co-op Translator](https://github.com/Azure/co-op-translator) کا استعمال کرتے ہوئے ترجمہ کی گئی ہے۔ ہم درستگی کے لیے کوشش کرتے ہیں، لیکن براہ کرم آگاہ رہیں کہ خودکار ترجمے میں غلطیاں یا غیر درستیاں ہو سکتی ہیں۔ اصل دستاویز کو اس کی اصل زبان میں مستند ذریعہ سمجھا جانا چاہیے۔ اہم معلومات کے لیے، پیشہ ور انسانی ترجمہ کی سفارش کی جاتی ہے۔ ہم اس ترجمے کے استعمال سے پیدا ہونے والی کسی بھی غلط فہمی یا غلط تشریح کے ذمہ دار نہیں ہیں۔
یہ دستاویز AI ترجمہ سروس [Co-op Translator](https://github.com/Azure/co-op-translator) کا استعمال کرتے ہوئے ترجمہ کی گئی ہے۔ ہم درستگی کے لیے کوشش کرتے ہیں، لیکن براہ کرم آگاہ رہیں کہ خودکار ترجمے میں غلطیاں یا غیر درستیاں ہو سکتی ہیں۔ اصل دستاویز کو اس کی اصل زبان میں مستند ذریعہ سمجھا جانا چاہیے۔ اہم معلومات کے لیے، پیشہ ور انسانی ترجمہ کی سفارش کی جاتی ہے۔ اس ترجمے کے استعمال سے پیدا ہونے والی کسی بھی غلط فہمی یا غلط تشریح کے لیے ہم ذمہ دار نہیں ہیں۔
# Xây dựng Ứng dụng Ngân hàng Phần 4: Khái niệm Quản lý Trạng thái
# Xây dựng ứng dụng ngân hàng Phần 4: Khái niệm về Quản lý trạng thái
## Câu hỏi trước bài giảng
@ -15,13 +15,13 @@ CO_OP_TRANSLATOR_METADATA:
### Giới thiệu
Khi một ứng dụng web phát triển, việc theo dõi tất cả các luồng dữ liệu trở nên thách thức. Mã nào lấy dữ liệu, trang nào sử dụng nó, dữ liệu cần được cập nhật ở đâu và khi nào... rất dễ dẫn đến mã lộn xộn và khó bảo trì. Điều này đặc biệt đúng khi bạn cần chia sẻ dữ liệu giữa các trang khác nhau trong ứng dụng, chẳng hạn như dữ liệu người dùng. Khái niệm *quản lý trạng thái* luôn tồn tại trong mọi loại chương trình, nhưng khi các ứng dụng web ngày càng phức tạp, đây trở thành một điểm quan trọng cần suy nghĩ trong quá trình phát triển.
Khi một ứng dụng web phát triển, việc theo dõi tất cả các luồng dữ liệu trở nên thách thức. Mã nào nhận dữ liệu, trang nào sử dụng nó, nơi nào và khi nào cần cập nhật... rất dễ dẫn đến mã lộn xộn và khó bảo trì. Điều này đặc biệt đúng khi bạn cần chia sẻ dữ liệu giữa các trang khác nhau của ứng dụng, ví dụ như dữ liệu người dùng. Khái niệm *quản lý trạng thái* luôn tồn tại trong mọi loại chương trình, nhưng khi các ứng dụng web ngày càng phức tạp, đây trở thành một điểm quan trọng cần suy nghĩ trong quá trình phát triển.
Trong phần cuối này, chúng ta sẽ xem xét lại ứng dụng đã xây dựng để suy nghĩ lại cách quản lý trạng thái, cho phép hỗ trợ làm mới trình duyệt tại bất kỳ thời điểm nào và duy trì dữ liệu qua các phiên người dùng.
### Điều kiện tiên quyết
Bạn cần hoàn thành phần [lấy dữ liệu](../3-data/README.md) của ứng dụng web trong bài học này. Bạn cũng cần cài đặt [Node.js](https://nodejs.org) và [chạy API máy chủ](../api/README.md) cục bộ để quản lý dữ liệu tài khoản.
Bạn cần hoàn thành phần [lấy dữ liệu](../3-data/README.md) của ứng dụng web cho bài học này. Bạn cũng cần cài đặt [Node.js](https://nodejs.org) và [chạy API máy chủ](../api/README.md) cục bộ để quản lý dữ liệu tài khoản.
Bạn có thể kiểm tra xem máy chủ có chạy đúng không bằng cách thực hiện lệnh này trong terminal:
@ -39,25 +39,25 @@ Trong [bài học trước](../3-data/README.md), chúng ta đã giới thiệu
Có 3 vấn đề với mã hiện tại:
- Trạng thái không được duy trì, vì làm mới trình duyệt sẽ đưa bạn trở lại trang đăng nhập.
- Có nhiều hàm sửa đổi trạng thái. Khi ứng dụng phát triển, điều này có thể khiến việc theo dõi các thay đổi trở nên khó khăn và dễ quên cập nhật một số phần.
- Có nhiều hàm sửa đổi trạng thái. Khi ứng dụng phát triển, điều này có thể khiến việc theo dõi các thay đổi trở nên khó khăn và dễ quên cập nhật một hàm.
- Trạng thái không được dọn dẹp, vì vậy khi bạn nhấp vào *Đăng xuất*, dữ liệu tài khoản vẫn còn đó mặc dù bạn đang ở trang đăng nhập.
Chúng ta có thể cập nhật mã để giải quyết từng vấn đề này, nhưng điều đó sẽ tạo ra sự trùng lặp mã và làm cho ứng dụng phức tạp hơn và khó bảo trì hơn. Hoặc chúng ta có thể dừng lại vài phút và suy nghĩ lại chiến lược của mình.
Chúng ta có thể cập nhật mã để giải quyết từng vấn đề một, nhưng điều này sẽ tạo ra sự trùng lặp mã và làm cho ứng dụng phức tạp hơn và khó bảo trì hơn. Hoặc chúng ta có thể dừng lại vài phút và suy nghĩ lại chiến lược của mình.
> Vấn đề thực sự mà chúng ta đang cố gắng giải quyết ở đây là gì?
> Những vấn đề thực sự chúng ta đang cố gắng giải quyết ở đây là gì?
[Quản lý trạng thái](https://en.wikipedia.org/wiki/State_management) là tất cả về việc tìm ra một cách tiếp cận tốt để giải quyết hai vấn đề cụ thể này:
[Quản lý trạng thái](https://en.wikipedia.org/wiki/State_management) là tất cả về việc tìm một cách tiếp cận tốt để giải quyết hai vấn đề cụ thể này:
- Làm thế nào để giữ cho các luồng dữ liệu trong ứng dụng dễ hiểu?
- Làm thế nào để giữ cho dữ liệu trạng thái luôn đồng bộ với giao diện người dùng (và ngược lại)?
Khi bạn đã giải quyết được những vấn đề này, bất kỳ vấn đề nào khác mà bạn có thể gặp phải có thể đã được khắc phục hoặc trở nên dễ dàng hơn để giải quyết. Có nhiều cách tiếp cận khác nhau để giải quyết những vấn đề này, nhưng chúng ta sẽ chọn một giải pháp phổ biến bao gồm **tập trung hóa dữ liệu và cách thay đổi nó**. Các luồng dữ liệu sẽ diễn ra như sau:
Khi bạn đã giải quyết được những vấn đề này, bất kỳ vấn đề nào khác bạn có thể gặp phải có thể đã được giải quyết hoặc trở nên dễ dàng hơn để giải quyết. Có nhiều cách tiếp cận khả thi để giải quyết những vấn đề này, nhưng chúng ta sẽ chọn một giải pháp phổ biến bao gồm **tập trung hóa dữ liệu và cách thay đổi nó**. Các luồng dữ liệu sẽ diễn ra như sau:

> Chúng ta sẽ không đề cập đến phần mà dữ liệu tự động kích hoạt cập nhật giao diện, vì nó liên quan đến các khái niệm nâng cao hơn về [Lập trình Phản ứng](https://en.wikipedia.org/wiki/Reactive_programming). Đây là một chủ đề tiếp theo tốt nếu bạn muốn tìm hiểu sâu hơn.
> Chúng ta sẽ không đề cập đến phần dữ liệu tự động kích hoạt cập nhật giao diện, vì nó liên quan đến các khái niệm nâng cao hơn về [Lập trình phản ứng](https://en.wikipedia.org/wiki/Reactive_programming). Đây là một chủ đề tiếp theo tốt nếu bạn muốn tìm hiểu sâu hơn.
✅ Có rất nhiều thư viện ngoài kia với các cách tiếp cận khác nhau để quản lý trạng thái, [Redux](https://redux.js.org) là một lựa chọn phổ biến. Hãy xem các khái niệm và mẫu được sử dụng vì đây thường là một cách tốt để học về các vấn đề tiềm năng mà bạn có thể gặp phải trong các ứng dụng web lớn và cách giải quyết chúng.
✅ Có rất nhiều thư viện ngoài kia với các cách tiếp cận khác nhau về quản lý trạng thái, [Redux](https://redux.js.org) là một lựa chọn phổ biến. Hãy xem các khái niệm và mẫu được sử dụng vì nó thường là một cách tốt để học về các vấn đề tiềm năng bạn có thể gặp phải trong các ứng dụng web lớn và cách giải quyết chúng.
### Nhiệm vụ
@ -75,9 +75,9 @@ let state = {
};
```
Ý tưởng là *tập trung hóa* tất cả dữ liệu ứng dụng của chúng ta trong một đối tượng trạng thái duy nhất. Hiện tại chúng ta chỉ có `account` trong trạng thái nên nó không thay đổi nhiều, nhưng nó tạo ra một con đường cho các cải tiến sau này.
Ý tưởng là *tập trung hóa* tất cả dữ liệu ứng dụng của chúng ta trong một đối tượng trạng thái duy nhất. Hiện tại chúng ta chỉ có `account` trong trạng thái nên không thay đổi nhiều, nhưng nó tạo ra một con đường cho các cải tiến.
Chúng ta cũng cần cập nhật các hàm sử dụng nó. Trong các hàm `register()` và `login()`, thay thế `account = ...` bằng `state.account = ...`;
Chúng ta cũng phải cập nhật các hàm sử dụng nó. Trong các hàm `register()` và `login()`, thay thế `account = ...` bằng `state.account = ...`;
Ở đầu hàm `updateDashboard()`, thêm dòng này:
@ -91,7 +91,7 @@ Việc tái cấu trúc này tự nó không mang lại nhiều cải tiến, nh
Bây giờ chúng ta đã thiết lập đối tượng `state` để lưu trữ dữ liệu, bước tiếp theo là tập trung hóa các cập nhật. Mục tiêu là làm cho việc theo dõi bất kỳ thay đổi nào và khi nào chúng xảy ra trở nên dễ dàng hơn.
Để tránh việc thay đổi đối tượng `state`, cũng là một thực hành tốt để coi nó là [*bất biến*](https://en.wikipedia.org/wiki/Immutable_object), nghĩa là nó không thể bị sửa đổi. Điều này cũng có nghĩa là bạn phải tạo một đối tượng trạng thái mới nếu muốn thay đổi bất kỳ điều gì trong đó. Bằng cách làm như vậy, bạn xây dựng một lớp bảo vệ chống lại các [tác dụng phụ](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) không mong muốn, và mở ra khả năng cho các tính năng mới trong ứng dụng của bạn như triển khai hoàn tác/làm lại, đồng thời làm cho việc gỡ lỗi trở nên dễ dàng hơn. Ví dụ, bạn có thể ghi lại mọi thay đổi được thực hiện đối với trạng thái và giữ lịch sử các thay đổi để hiểu nguồn gốc của lỗi.
Để tránh việc thay đổi đối tượng `state`, cũng là một thực hành tốt để coi nó là [*bất biến*](https://en.wikipedia.org/wiki/Immutable_object), nghĩa là nó không thể bị sửa đổi. Điều này cũng có nghĩa là bạn phải tạo một đối tượng trạng thái mới nếu muốn thay đổi bất kỳ điều gì trong đó. Bằng cách làm điều này, bạn xây dựng một sự bảo vệ chống lại các [tác động phụ](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) không mong muốn, và mở ra các khả năng cho các tính năng mới trong ứng dụng của bạn như triển khai hoàn tác/làm lại, đồng thời làm cho việc gỡ lỗi trở nên dễ dàng hơn. Ví dụ, bạn có thể ghi lại mọi thay đổi được thực hiện đối với trạng thái và giữ lịch sử các thay đổi để hiểu nguồn gốc của lỗi.
Trong JavaScript, bạn có thể sử dụng [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) để tạo một phiên bản bất biến của một đối tượng. Nếu bạn cố gắng thực hiện thay đổi đối với một đối tượng bất biến, một ngoại lệ sẽ được đưa ra.
@ -110,7 +110,7 @@ function updateState(property, newData) {
}
```
Trong hàm này, chúng ta tạo một đối tượng trạng thái mới và sao chép dữ liệu từ trạng thái trước đó bằng cách sử dụng [*toán tử spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Sau đó, chúng ta ghi đè một thuộc tính cụ thể của đối tượng trạng thái với dữ liệu mới bằng cách sử dụng [cú pháp ngoặc vuông](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` để gán giá trị. Cuối cùng, chúng ta khóa đối tượng để ngăn chặn các sửa đổi bằng `Object.freeze()`. Hiện tại chúng ta chỉ có thuộc tính `account` được lưu trữ trong trạng thái, nhưng với cách tiếp cận này, bạn có thể thêm bao nhiêu thuộc tính tùy ý vào trạng thái.
Trong hàm này, chúng ta tạo một đối tượng trạng thái mới và sao chép dữ liệu từ trạng thái trước đó bằng [*toán tử spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Sau đó, chúng ta ghi đè một thuộc tính cụ thể của đối tượng trạng thái với dữ liệu mới bằng [cú pháp ngoặc vuông](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` để gán. Cuối cùng, chúng ta khóa đối tượng để ngăn chặn các sửa đổi bằng `Object.freeze()`. Hiện tại chúng ta chỉ có thuộc tính `account` được lưu trữ trong trạng thái, nhưng với cách tiếp cận này bạn có thể thêm bao nhiêu thuộc tính tùy thích vào trạng thái.
Chúng ta cũng sẽ cập nhật khởi tạo `state` để đảm bảo trạng thái ban đầu cũng được đóng băng:
@ -120,7 +120,7 @@ let state = Object.freeze({
});
```
Sau đó, cập nhật hàm `register` bằng cách thay thế `state.account = result;`bằng:
Sau đó, cập nhật hàm `register` bằng cách thay thế `state.account = result;`với:
```js
updateState('account', result);
@ -132,7 +132,7 @@ Làm tương tự với hàm `login`, thay thế `state.account = data;` bằng:
updateState('account', data);
```
Chúng ta sẽ nhân cơ hội này để sửa lỗi dữ liệu tài khoản không được xóa khi người dùng nhấp vào *Đăng xuất*.
Chúng ta sẽ tận dụng cơ hội này để sửa lỗi dữ liệu tài khoản không được xóa khi người dùng nhấp vào *Đăng xuất*.
Tạo một hàm mới `logout()`:
@ -143,41 +143,41 @@ function logout() {
}
```
Trong `updateDashboard()`, thay thế chuyển hướng `return navigate('/login');` bằng `return logout();`
Trong `updateDashboard()`, thay thế chuyển hướng `return navigate('/login');` bằng `return logout()`;
Hãy thử đăng ký một tài khoản mới, đăng xuất và đăng nhập lại để kiểm tra xem mọi thứ vẫn hoạt động chính xác.
Hãy thử đăng ký một tài khoản mới, đăng xuất và đăng nhập lại để kiểm tra rằng mọi thứ vẫn hoạt động đúng.
> Mẹo: bạn có thể xem tất cả các thay đổi trạng thái bằng cách thêm `console.log(state)` ở cuối `updateState()` và mở bảng điều khiển trong công cụ phát triển của trình duyệt.
## Duy trì trạng thái
Hầu hết các ứng dụng web cần duy trì dữ liệu để hoạt động chính xác. Tất cả dữ liệu quan trọng thường được lưu trữ trong cơ sở dữ liệu và truy cập thông qua API máy chủ, như dữ liệu tài khoản người dùng trong trường hợp của chúng ta. Nhưng đôi khi, cũng rất hữu ích để duy trì một số dữ liệu trên ứng dụng khách đang chạy trong trình duyệt của bạn, để cải thiện trải nghiệm người dùng hoặc tăng hiệu suất tải.
Hầu hết các ứng dụng web cần duy trì dữ liệu để hoạt động đúng. Tất cả dữ liệu quan trọng thường được lưu trữ trên cơ sở dữ liệu và truy cập thông qua API máy chủ, như dữ liệu tài khoản người dùng trong trường hợp của chúng ta. Nhưng đôi khi, cũng rất thú vị để duy trì một số dữ liệu trên ứng dụng khách đang chạy trong trình duyệt, để có trải nghiệm người dùng tốt hơn hoặc cải thiện hiệu suất tải.
Khi bạn muốn duy trì dữ liệu trong trình duyệt, có một số câu hỏi quan trọng bạn nên tự hỏi:
- *Dữ liệu có nhạy cảm không?* Bạn nên tránh lưu trữ bất kỳ dữ liệu nhạy cảm nào trên ứng dụng khách, chẳng hạn như mật khẩu người dùng.
- *Bạn cần giữ dữ liệu này trong bao lâu?* Bạn có định truy cập dữ liệu này chỉ trong phiên hiện tại hay muốn lưu trữ nó mãi mãi?
- *Bạn cần giữ dữ liệu này trong bao lâu?* Bạn có dự định truy cập dữ liệu này chỉ trong phiên hiện tại hay muốn nó được lưu trữ mãi mãi?
Có nhiều cách để lưu trữ thông tin trong một ứng dụng web, tùy thuộc vào những gì bạn muốn đạt được. Ví dụ, bạn có thể sử dụng URL để lưu trữ một truy vấn tìm kiếm và làm cho nó có thể chia sẻ giữa các người dùng. Bạn cũng có thể sử dụng [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) nếu dữ liệu cần được chia sẻ với máy chủ, như thông tin [xác thực](https://en.wikipedia.org/wiki/Authentication).
Một tùy chọn khác là sử dụng một trong nhiều API trình duyệt để lưu trữ dữ liệu. Hai API đặc biệt thú vị:
Một tùy chọn khác là sử dụng một trong nhiều API trình duyệt để lưu trữ dữ liệu. Hai trong số đó đặc biệt thú vị:
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): một [Kho lưu trữ Key/Value](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) cho phép duy trì dữ liệu cụ thể cho trang web hiện tại qua các phiên khác nhau. Dữ liệu được lưu trong đó không bao giờ hết hạn.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): hoạt động giống như `localStorage` ngoại trừ việc dữ liệu được lưu trong đó sẽ bị xóa khi phiên kết thúc (khi trình duyệt bị đóng).
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): cái này hoạt động giống như `localStorage` ngoại trừ việc dữ liệu được lưu trữ trong đó sẽ bị xóa khi phiên kết thúc (khi trình duyệt bị đóng).
Lưu ý rằng cả hai API này chỉ cho phép lưu trữ [chuỗi](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Nếu bạn muốn lưu trữ các đối tượng phức tạp, bạn sẽ cần tuần tự hóa chúng sang định dạng [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) bằng [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Lưu ý rằng cả hai API này chỉ cho phép lưu trữ [chuỗi](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Nếu bạn muốn lưu trữ các đối tượng phức tạp, bạn sẽ cần tuần tự hóa nó sang định dạng [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) bằng [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Nếu bạn muốn tạo một ứng dụng web không làm việc với máy chủ, cũng có thể tạo một cơ sở dữ liệu trên ứng dụng khách bằng API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). API này dành cho các trường hợp sử dụng nâng cao hoặc nếu bạn cần lưu trữ một lượng lớn dữ liệu, vì nó phức tạp hơn để sử dụng.
✅ Nếu bạn muốn tạo một ứng dụng web không hoạt động với máy chủ, cũng có thể tạo một cơ sở dữ liệu trên ứng dụng khách bằng API [`IndexedDB`](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Cái này dành cho các trường hợp sử dụng nâng cao hoặc nếu bạn cần lưu trữ lượng dữ liệu đáng kể, vì nó phức tạp hơn để sử dụng.
### Nhiệm vụ
Chúng ta muốn người dùng của mình vẫn đăng nhập cho đến khi họ nhấp rõ ràng vào nút *Đăng xuất*, vì vậy chúng ta sẽ sử dụng `localStorage` để lưu trữ dữ liệu tài khoản. Đầu tiên, hãy định nghĩa một khóa mà chúng ta sẽ sử dụng để lưu trữ dữ liệu của mình.
Chúng ta muốn người dùng vẫn đăng nhập cho đến khi họ nhấp vào nút *Đăng xuất*, vì vậy chúng ta sẽ sử dụng `localStorage` để lưu trữ dữ liệu tài khoản. Đầu tiên, hãy định nghĩa một khóa mà chúng ta sẽ sử dụng để lưu trữ dữ liệu của mình.
```js
const storageKey = 'savedAccount';
```
Sau đó thêm dòng này vào cuối hàm `updateState()`:
Sau đó, thêm dòng này vào cuối hàm `updateState()`:
Với điều này, dữ liệu tài khoản người dùng sẽ được duy trì và luôn cập nhật vì chúng ta đã tập trung hóa tất cả các cập nhật trạng thái trước đó. Đây là lúc chúng ta bắt đầu hưởng lợi từ tất cả các lần tái cấu trúc trước đó 🙂.
Vì dữ liệu được lưu, chúng ta cũng cần khôi phục nó khi ứng dụng được tải. Vì chúng ta sẽ bắt đầu có nhiều mã khởi tạo hơn, có thể là một ý tưởng hay để tạo một hàm `init` mới, bao gồm cả mã trước đó ở cuối `app.js`:
Vì dữ liệu đã được lưu, chúng ta cũng phải xử lý việc khôi phục nó khi ứng dụng được tải. Vì chúng ta sẽ bắt đầu có nhiều mã khởi tạo hơn, có thể là một ý tưởng tốt để tạo một hàm mới `init`, bao gồm cả mã trước đó ở cuối `app.js`:
```js
function init() {
@ -202,11 +202,11 @@ function init() {
init();
```
Ở đây chúng ta lấy dữ liệu đã lưu, và nếu có, chúng ta cập nhật trạng thái tương ứng. Điều quan trọng là làm điều này *trước* khi cập nhật tuyến đường, vì có thể có mã phụ thuộc vào trạng thái trong quá trình cập nhật trang.
Ở đây chúng ta lấy dữ liệu đã lưu, và nếu có, chúng ta cập nhật trạng thái tương ứng. Điều quan trọng là làm điều này *trước* khi cập nhật tuyến đường, vì có thể có mã dựa vào trạng thái trong quá trình cập nhật trang.
Chúng ta cũng có thể làm cho trang *Dashboard* trở thành trang mặc định của ứng dụng, vì bây giờ chúng ta đang duy trì dữ liệu tài khoản. Nếu không tìm thấy dữ liệu, bảng điều khiển sẽ tự động chuyển hướng đến trang *Login*. Trong `updateRoute()`, thay thế dự phòng `return navigate('/login');` bằng `return navigate('/dashboard');`.
Chúng ta cũng có thể làm cho trang *Dashboard* trở thành trang mặc định của ứng dụng, vì bây giờ chúng ta đang duy trì dữ liệu tài khoản. Nếu không tìm thấy dữ liệu, bảng điều khiển sẽ xử lý việc chuyển hướng đến trang *Login* dù sao đi nữa. Trong `updateRoute()`, thay thế dự phòng `return navigate('/login');` bằng `return navigate('/dashboard');`.
Bây giờ hãy đăng nhập vào ứng dụng và thử làm mới trang. Bạn sẽ vẫn ở trên bảng điều khiển. Với bản cập nhật này, chúng ta đã giải quyết tất cả các vấn đề ban đầu...
Bây giờ đăng nhập vào ứng dụng và thử làm mới trang. Bạn sẽ vẫn ở trên bảng điều khiển. Với bản cập nhật đó, chúng ta đã giải quyết tất cả các vấn đề ban đầu...
Thử làm mới trang bảng điều khiển trong trình duyệt bây giờ. Điều gì xảy ra? Bạn có thấy giao dịch mới không?
Thử làm mới trang bảng điều khiển của bạn trong trình duyệt bây giờ. Điều gì xảy ra? Bạn có thấy giao dịch mới không?
Trạng thái được duy trì vô thời hạn nhờ `localStorage`, nhưng điều đó cũng có nghĩa là nó không bao giờ được cập nhật cho đến khi bạn đăng xuất khỏi ứng dụng và đăng nhập lại!
Một chiến lược khả thi để khắc phục điều đó là tải lại dữ liệu tài khoản mỗi khi bảng điều khiển được tải, để tránh dữ liệu bị lỗi thời.
Một chiến lược khả thi để khắc phục điều đó là tải lại dữ liệu tài khoản mỗi khi bảng điều khiển được tải, để tránh dữ liệu bị trì trệ.
### Nhiệm vụ
@ -247,9 +247,9 @@ async function updateAccountData() {
}
```
Phương thức này kiểm tra xem chúng ta hiện đang đăng nhập hay không, sau đó tải lại dữ liệu tài khoản từ máy chủ.
Phương pháp này kiểm tra rằng chúng ta hiện đang đăng nhập sau đó tải lại dữ liệu tài khoản từ máy chủ.
Tạo một hàm khác có tên `refresh`:
Tạo một hàm khác tên là`refresh`:
```js
async function refresh() {
@ -258,7 +258,7 @@ async function refresh() {
}
```
Hàm này cập nhật dữ liệu tài khoản, sau đó xử lý việc cập nhật HTML của trang bảng điều khiển. Đây là hàm chúng ta cần gọi khi tuyến đường bảng điều khiển được tải. Cập nhật định nghĩa tuyến đường với:
Hàm này cập nhật dữ liệu tài khoản, sau đó xử lý việc cập nhật HTML của trang bảng điều khiển. Đây là điều chúng ta cần gọi khi tuyến đường bảng điều khiển được tải. Cập nhật định nghĩa tuyến đường với:
```js
const routes = {
@ -267,7 +267,7 @@ const routes = {
};
```
Thử làm mới bảng điều khiển bây giờ, nó sẽ hiển thị dữ liệu tài khoản đã cập nhật.
Thử làm mới bảng điều khiển bây giờ, nó sẽ hiển thị dữ liệu tài khoản đã được cập nhật.
---
@ -275,20 +275,20 @@ Thử làm mới bảng điều khiển bây giờ, nó sẽ hiển thị dữ l
Bây giờ chúng ta tải lại dữ liệu tài khoản mỗi khi bảng điều khiển được tải, bạn có nghĩ rằng chúng ta vẫn cần duy trì *toàn bộ dữ liệu tài khoản* không?
Hãy thử làm việc cùng nhau để thay đổi những gì được lưu và tải từ `localStorage`để chỉ bao gồm những gì thực sự cần thiết cho ứng dụng hoạt động.
Hãy thử làm việc cùng nhau để thay đổi những gì được lưu và tải từ `localStorage` chỉ bao gồm những gì thực sự cần thiết để ứng dụng hoạt động.
## Câu hỏi sau bài giảng
[Câu hỏi sau bài giảng](https://ff-quizzes.netlify.app/web/quiz/48)
[Quiz sau bài giảng](https://ff-quizzes.netlify.app/web/quiz/48)
## Bài tập
[Thực hiện hộp thoại "Thêm giao dịch"](assignment.md)
Dưới đây là một ví dụ kết quả sau khi hoàn thành nhiệm vụ:
Dưới đây là một ví dụ kết quả sau khi hoàn thành bài tập:


---
**Tuyên bố miễn trừ trách nhiệm**:
Tài liệu này đã được dịch bằng dịch vụ dịch thuật AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn tham khảo chính thức. Đối với các thông tin quan trọng, nên sử dụng dịch vụ dịch thuật chuyên nghiệp từ con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.
Tài liệu này đã được dịch bằng dịch vụ dịch thuật AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn thông tin chính thức. Đối với các thông tin quan trọng, khuyến nghị sử dụng dịch vụ dịch thuật chuyên nghiệp bởi con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.
随着 Web 应用程序的规模不断扩大,跟踪所有数据流变得越来越具有挑战性。哪些代码获取数据,哪些页面使用数据,数据需要在何时何地更新……很容易导致代码混乱,难以维护。尤其是当你需要在应用程序的不同页面之间共享数据时,比如用户数据。*状态管理*的概念一直存在于各种程序中,但随着 Web 应用程序的复杂性不断增加,它现在成为开发过程中需要重点考虑的问题。
✅ 如果你想创建一个不依赖服务器的 Web 应用程序,也可以使用 [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) 在客户端创建数据库。这个 API 适用于高级用例或需要存储大量数据的情况,因为它使用起来更复杂。