הרשמה לניוזלטר

הרשמה לניוזלטר

הירשמו לקבלת תוכן איכותי מעולם הפרונט לתיבת הדואר הנכנס, כל חודש.

Ⓒ כל הזכויות שמורות ל- Fed Cast – קהילת מפתחי הפרונט בישראל

ריאקטיביות עם Proxy

ריאקטיביות עם Proxy

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

ריאקטיביות מהווה אתגר משמעותי בפיתוח, אך התוצאות יכולות להיות פנטסטיות. במאמר הבא נממש ריאקטיביות עם Proxy, שהוא כלי עוצמתי בכלל ובפרט למטרה שלנו.

מה זה Proxy API ואיך הוא עובד?

Proxy מאפשר לנו ״לתפוס״ קריאה או שינוי של מידע באובייקט, ובכך גם לכפות לוגיקה מותאמת אישית בכל אחת מהפעולות הללו.

Proxy מקבל שני משתנים: אובייקט המטרה שלנו (האובייקט שעליו נאזין לקריאה או שינוי) ואובייקט הגדרות (handler) שמכיל פונקציות get & set המגדירות איך הפרוקסי יתנהג בזמן קריאה או שינוי אובייקט המטרה.

לדוגמה, בהגדרה של פונקציה get בתוך ה handler, אנו יכולים לכתוב קוד שירוץ בכל פעם שננסה לקרוא ערך מתוך פרופ של אובייקט המטרה. באמצעות הפונקציה set אפשר להגדיר קוד שירוץ ברגע שנרצה לשנות את ערך הפרופ באובייקט המטרה.

יתרונות של Proxy לבניית ריאקטיביות

בבניית ריאקטיביות עם Proxy ישנם מספר יתרונות:

  1. שמירה על נקיון הקוד: השימוש ב Proxy מאפשר לנו לבנות קוד יציב וקריא, כאשר תגובת הממשק הריאקטיבי נמצאת במקום אחד בלבד.
  2. אפשרויות מותאמות אישית: היכולת להגדיר התנהגות רצויה במגוון תרחישים שונים מאפשרת דינמיות וגמישות.
  3. הוספת פונקציונליות מתקדמת: בעזרת פרוקסי ניתן להוסיף תכונות מתקדמות, טכניקות בדיקה ושיפורים נוספים לממשק.

איך זה נראה בקוד?

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

// Creating a simple object that holds a state. Will be used as the target object.
const data = {
  counter: 0
};

// Configure the handler object and how we are going to follow changes on state and update the DOM.
const handler = {
  get(target, property) {
    // Get the value of a specific property
    return target[property];
  },
  set(target, property, value) {
    // Set the value of a specific property
    target[property] = value;

    // Update the DOM
    updateView();
  }
};

// Creating the Proxy
const reactiveData = new Proxy(data, handler);

// Function for updating the DOM
function updateView() {
  const counterElement = document.getElementById('counter');
  if (counterElement) {
    counterElement.textContent = reactiveData.counter;
  }
}

// Listen to click event to set the new counter value
const incrementButton = document.getElementById('increment');
if (incrementButton) {
  incrementButton.addEventListener('click', () => {
    reactiveData.counter++;
  });
}

// Initial DOM update
updateView();

בדוגמה זו, יצרתי אובייקט Proxy עבור הסטייט השמור בתוך המשתנה data. כאשר הערך של פרופ מסויים משתנה, הפונקציה set ב-handler מופעלת והיא מעדכנת את התצוגה באופן אוטומטי.

ברגע שערך הפרופ counter משתנה בעת לחיצה על כפתור Increment, התצוגה מתעדכנת ומציגה את הערך החדש.

וככה זה נראה בפועל ->

ריאקטיביות בסקייל

עד כה הצלחנו להציג דוגמא עובדת של ריאקטיביות דרך עדכוני DOM על כל שינוי שמתבצע בסטייט, אבל איך מוסיפים סקייל ועוד קומפוננטות?

כדי לעשות זאת מימשתי EventBus דרכו נרשמות קומפוננטות לשינויים של הסטייט ובעצם על כל שינוי כל קומפוננטה תשקף את השינוי על האלמנט שלה.

ברמת העיקרון באותה פונקציית update כל קומפוננטה יכול לממש כל לוגיקה שנרצה, במקרה שלנו הלוגיקה זהה ופשוטה וסך הכל מעדכנת את ה DOM.

class EventBus {
  // List of all subscribed components
  subscribers = new Set();
  
  // Add subscriber
  subscribe(subscriberCallback) {
    this.subscribers.add(subscriberCallback);
    subscriberCallback(reactiveData.counter);
  }
  
  // Remove subscriber
  unsubscribe(subscriberCallback) {
    this.subscribers.delete(subscriberCallback);
  }

  // Letting the subscribers know about new change
  publish() {
    this.subscribers.forEach(subscriberCallback => subscriberCallback(reactiveData.counter));
  }
}

const eventBus = new EventBus();

// Initial DOM update
eventBus.publish();

function renderCounterComponent() {
  // Rendering a counter component that shows the counter number and also a button to remove itself.
  const counter = document.createElement('div');
  counter.classList.add('counter');
  
  const counterText = document.createElement('span');
  counter.appendChild(counterText);
  function updateCounter(count) {
    // Updating the counter text.
    counterText.innerText = count;
  }
  
  const removeCounterBtn = document.createElement('button');
  removeCounterBtn.innerText = '❌';
  removeCounterBtn.addEventListener('click', () => {
    eventBus.unsubscribe(updateCounter);
    counter.remove();
  })
  counter.appendChild(removeCounterBtn);
  
  eventBus.subscribe(updateCounter);
  document.body.querySelector('.counters-container').appendChild(counter);
}

const addCounter = document.getElementById('add-counter');
addCounter.addEventListener('click', renderCounterComponent);

והתוצאה לפניכם

סיכום

כיום, לא מעט ספריות ופריימוורקים משתמשים ב Proxy בכדי לעקוב אחר שינויים בסטייט ולעדכן את התצוגה בהתאם, למשל – Vue, SolidJS, Immer ועוד…

אז כפי שהבנתם, Proxy, שהוצג לראשונה ב-ES6 (ECMAScript 2015), הוא כלי עוצמתי בבניית ריאקטיביות. השימוש בו מאפשר לנו להפעיל תגובה מותאמת אישית בעת שינויים בנתונים. כל היתרונות הללו עוזרים לנו לכתוב קוד יעיל, קריא וקל לתחזוקה.

שלום לך 👋 נעים להכיר.

הירשמו לקבלת תוכן איכותי מעולם הפרונט לתיבת הדואר הנכנס, כל חודש.

אנחנו לא שולחים ספאם!

רוצים לקבל מאיתנו עדכונים?

אם עולם הפרונט מעניין אתכם ואתם רוצים לקבל עדכונים ישירות למייל על כל המאמרים הכי מעניינים, המשרות החדשות, הפודקאסטים הכי נשמעים ועוד הרבה תוכן משובח, הירשמו לניוזלטר שלנו והישארו מעודכנים!

הרשמה לניוזלטר

הירשמו לקבלת תוכן איכותי מעולם הפרונט לתיבת הדואר הנכנס, כל חודש.