שתי דוגמאות ל Vue Hooks כדי להבין איך זה עובד

04/11/2021

אחד הקונספטים שעבר כמעט בשלמותו מ React ל Vue היה ה Hooks, ובפרט הרעיון שאפשר לקחת לוגיקה שמשפיעה על State של קומפוננטה, להוציא החוצה את הלוגיקה ואת ה State שמחובר אליה לפונקציה חיצונית ואז להשתמש במנגנון מכל מיני קומפוננטות. בפוסט זה אני רוצה להראות שתי דוגמאות לכתיבת פונקציות חיצוניות כאלה ב Vue ודרכן את היכולות הריאקטיביות של הספריה. בדוגמה הראשונה נראה פונקציה שמחברת בין קומפוננטה לגודל החלון ובדוגמה השניה פונקציה ששומרת משתנה ב Local Storage כדי שהערך שלו תמיד ימשיך מאותה נקודה.

1. הפונקציה useWindowSize

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

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

ה Hook במקרה שלנו יכלול את הלוגיקה הבאה:

  1. צור משתנה ריאקטיבי עם שני שדות עבור רוחב וגובה של החלון.

  2. כשהקומפוננטה נכנסת לעמוד תוסיף Event Handler ל Window שכל פעם שהגודל שלו משתנה יעדכן את המשתנים הריאקטיביים.

  3. כשהקומפוננטה יוצאת מהעמוד תמחק את ה Event Handler.

סך הכל קוד הפונקציה נראה כך:


export function useWindowSize() {
  const size = reactive({ width: window.innerWidth, height: window.innerHeight });

  function handleResize() {
    size.width = window.innerWidth;
    size.height = window.innerHeight;
  }

  onMounted(() => {
    window.addEventListener('resize', handleResize);
  });

  onUnmounted(() => {
    window.removeEventListener('resize', resizeHandler);
  });

  return size;
}

ובשביל להשתמש ב Hook מתוך קומפוננטה אנחנו רק צריכים להפעיל את הפונקציה:

<script setup>
import { useWindowSize } from './hooks/hooks';

const size = useWindowSize();

</script>

<template>
  <div>
    <p>Window width = {{size.width}}; height = {{size.height}}</p>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

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

2. יצירת משתנה ריאקטיבי ששומר על ערכו

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

הלוגיקה של ה Hook היא לכן:

  1. ביצירת הקומפוננטה צור משתנה ריאקטיבי והתחל לעקוב אחר שינויים בו.

  2. כל פעם שהערך משתנה כתוב את הערך החדש ל Local Storage.

  3. ביציאת הקומפוננטה מהמסך הפסק להקשיב לשינויים במשתנה הריאקטיבי.

הפונקציה היא לכן בסך הכל:

export function useLocalStorage(key, initialValue) {
  const value = ref(localStorage.getItem(key) || initialValue);
  const unwatch = watch(value, (currentValue, oldValue) => {
    localStorage.setItem(key, currentValue);    
  });

  onUnmounted(() => {
    unwatch();
    unwatch = null;
  });

  return value;
}

ובשביל להשתמש בה אפשר לכתוב קומפוננטה כמו זו:

<script setup>
import { useLocalStorage } from './hooks/hooks';

const value = useLocalStorage("counter", 0);

</script>

<template>
  <div>
  <button @click="() => value++">{{value}}</button>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

הקומפוננטה מציגה מונה וכל פעם שלוחצים על הכפתור הערך עולה ב-1. ביציאה מהדפדפן או טעינה מחדש של העמוד הערך של המונה נטען מ Local Storage וכך המונה ממשיך מאותה נקודה.

סך הכל השימוש ב Hooks ב Vue מאוד פשוט. החיסרון היחיד שלו לדעתי בהשוואה לריאקט זה שהוא פחות מסודר: כל Hook יכול להאזין לכל אירוע Lifecycle ולהחליט מה עושים בתגובה לכל אירוע, בניגוד לריאקט שם הקוד מסודר כ"קוד להריץ בהתחלה" ו"קוד להריץ בסיום" בצירוף רשימה של תלויות. המבנה של Vue יותר פשוט לכתיבה אבל גם עלול ליצור באגים אם לא מטפלים נכון בכל האירועים.