דלגו לתוכן

NGRX State

פורסם בתאריך 23 באוקטובר 2023

This video explains the fundamentals of NGRX state management in Angular, highlighting key concepts and best practices with practical examples.

NGRX הוא ספריית STATE management, בשיעור זה נלמד את החוקים הבסיסיים על ה - state שמנוהל על ידי NGRX.

ה - state הוא Object שמוחזק על ידי NGRX.
Components יכולים לבחור להירשם לשינוי ב state, ולקבל התראה כאשר ה state משתנה, אז הם יציירו את ה - UI עם הנתונים החדשים.
Components יכולים גם לספר ל NGRX להחליף את ה state עם state חדש, כך שתמיד ניתן להחליף את ה state עם state חדש.
נוכל להביט ב state כגיליון נתונים בנקודת זמן מסוימת, וNGRX מחזיק את ה state הנוכחי.

בואו נבחן את ה state באפליקציית counter פשוטה זו:

counter app

האפליקציה תגדיל את המונה בכל לחיצה על כפתור.
ערך המונה מתקבל מ - NGRX.
באמצעות Redux Dev Tools אנו יכולים לבחון את ה state שמנוהל על ידי NGRX שמחזיק בצד הימני של המסך.
מה state פשוט כזה אנו יכולים להבין כמה עובדות על ה state שמנוהל על ידי NGRX, שהן גם נגזרות מבסיס של Redux - NGRX הוא אימפלמנטציה של Redux באמצעות Observables.

ה state שלנו הוא Object פשוט ב - Javascript

שימו לב שה state הוא Object פשוט שנראה כך:

{
"counter": 0
}

אותו Object מתחיל עם ערך ראשוני של 0.
ה- NGRX state יש ערך ראשוני שנצטרך לספק כאשר אנו מגדירים את NGRX (אותו דבר עם Redux עלינו לספק את הערך הראשוני של ה state)

ה - State הוא Immutable (בלתי ניתן לשינוי)

Section titled “ה - State הוא Immutable (בלתי ניתן לשינוי)”

ה- state שלנו מתחיל עם:

{
"couter": 0
}

ואחרי שהמשתמש לוחץ על כפתור ה - Increment, ה state משתנה ל:

{
"couter": 1
}

כאשר אנו בודקים את ה state הזה, ייתכן שנחשב שה state הוא mutable, אך זה לא נכון.
המצב הוא כזה שאנו לא משנים את הערך של המאפיין counter, אלא אנו בעצם מחליף את כל ה state עם state חדש.
להסבר עם קוד פסאודו:

// lets imaging we have the following imaginary function from ngrx
import {getState, setState} from '@ngrx/store';
/**
* this is an imaginary function called from pressing the increment button
*/
function increment() {
const currentState = getState();
// we are not doing
// currentState['counter'] = currentState['counter'] + 1
// we are doing
const newState = {
...currentState,
counter: currentState.counter + 1,
};
setState(newState);
}

בדוגמא זו יש לנו פונקציה לקבלת ה state הנוכחי, ופונקציה להגדרת state חדש.
אנחנו לא משנים את המאפיין counter ב state הנוכחי, אלא בונים state חדש מאפס, על ידי העתקת ה state הנוכחי, ומחליפים את המאפיין counter עם ערך חדש (בדוגמא זו כל כך פשוטה כי המונה הוא המאפיין היחיד, אך בדרך כלל ה state מכיל יותר מאפיין אחד ואז האופרטור המתאים הוא ה spread operator).

ה - state הוא immutable, זה אומר שאנו לא משנים את ה state הנוכחי, אלא בונים state חדש ומחליפים את ה state הנוכחי עם ה state החדש.

הסתכלות על שינוי ה- State מנקודת מבט של RXJS

Section titled “הסתכלות על שינוי ה- State מנקודת מבט של RXJS”

ישנה Service בחבילת הראשית של NGRX שנקרא Store שנדון בשיעורים הבאים.
אנו יכולים להביט ב - Service זה כ - Observable שמשדר את ה state בכל פעם שה state משתנה.

state as observable

המלצה: ה - State צריך להיות Serializable

Section titled “המלצה: ה - State צריך להיות Serializable”

המונח Serializable אומר שאנו יכולים להמיר את ה - Object למחרוזת, ואז להמיר את המחרוזת חזרה ל - Object, ולקבל Object דומה.
טריק טוב לבדוק אם Object הוא Serializable או לא הוא להשתמש ב - JSON.stringify ו - JSON.parse ולראות אם אנו מקבלים את אותו Object.
לדוגמא:

// this is an example of a serializable object
const obj = {
name: 'Yariv',
age: 37,
myBooks: [
{
title: 'The Hobbit',
author: 'J.R.R Tolkien',
},
],
};
const objString = JSON.stringify(obj);
const objFromString = JSON.parse(objString);
console.log(objFromString); // this will return the same obj

בדוגמא זו לקחנו Object, המירנו אותו למחרוזת, ואז המרנו את המחרוזת חזרה ל - Object, וקיבלנו את אותו Object.

להלן דוגמא ל - Object שאינו Serializable:

class Book {
constructor(public title: string, public author: string) {}
}
const obj = {
name: 'Yariv',
age: 37,
myFunctions: [function hello() {}],
};
const objString = JSON.stringify(obj);
const objFromString = JSON.parse(objString);
console.log(objFromString); // this will return an object without the functions in the array

אז בעצם Serializable אומר שהאובייקט שנוצר מפרימיטיבים פשוטים (מחרוזת, מספר, בוליאני) ומערכים ואובייקטים שנוצרים מפרימיטיבים פשוטים הוא Serializable.

זוהי רק המלצה וניתן להשתמש ב - NGRX לאחסון פונקציות, מחלקות וכו’… אך דברים כמו: נסיעה בזמן, אחסון של מצב המשתמש כדי לשחזר אותו לאותו מצב, יכול לא לעבוד כמו שצריך.

ה - State צריך להיות מאורגן

Section titled “ה - State צריך להיות מאורגן”

אנחנו לא מתייחסים ל - state כאל משתנה גלובלי אליו נזרוק בבלגן את כל המידע שלנו. אני מניח שלפני NGRX אתם אחסנתם את המידע של האפליקציה שלכם באמצעות Services, במקרה כזה לא יצרתם Service אחד לאחסון כל המידע, אלא יצרתם Service לפי הלוגיקה מסוימת. במידה והייתם מחזיקים מידע על משתמש, כנראה שיצרתם UserService לאחסון המידע של המשתמש עם פונקציות כמו לקבל את המשתמש מהשרת, לעדכן את המשתמש וכו’.
אותו דבר נכון גם עבור ה - state, עליכם לארגן את ה state על פי הלוגיקה של האפליקציה שלכם.
בואו נבחן את הדוגמא הבאה:

books app

שימו לב שבדוגמא זו ה state נראה כך:

{
"user": {
"firstName": "Yariv",
"lastName": "Katz",
"avatar": "..."
},
"books": {
list: [
{
"title": "The Hobbit",
"author": "J.R.R Tolkien"
"year": "1937"
}
]
}
}

שימו לב שה state מאורגן על פי הלוגיקה של האפליקציה. בנוסף ארגון ה state מוטמע עם Interface.

ניתן לטעון ולהסיר חלקים מה - State בצורה עצלנית

Section titled “ניתן לטעון ולהסיר חלקים מה - State בצורה עצלנית”

נעיף עוד מבט על אפליקציית הספרים:

books app

שימו לב שה state מתחיל עם:

{
"user": null
}

ורק אחרי שהמשתמש נכנס לאפליקציה יש לנו את הספרים ב state:

{
"user": {
"firstName": "Yariv",
"lastName": "Katz",
"avatar": "..."
},
"books": {
list: [
{
"title": "The Hobbit",
"author": "J.R.R Tolkien"
"year": "1937"
}
]
}
}

בדומה לכך שיש לנו Modules שמטעינים בצורה עצלנית, כלומר רק כאשר המשתמש נכנס לאותו מודול, כך גם ה state יכול להיות עצלני, כלומר נוכל לטעון חלקים מה state רק כאשר הם נדרשים.

בשיעור זה למדנו את החוקים הבסיסיים על ה state שמנוהל על ידי NGRX.
החוק הכי חשוב שצריך לקחת איתנו לשיעורים הבאים, הוא העובדה שה state הוא immutable.
בואו נקח את כל המידע הזה לשיעור הבא שנלמד על ה - Service שמחזיק את ה state - ה - Store.