• בלוג
  • ניסוי React - ואם היו Directives?

ניסוי React - ואם היו Directives?

02/11/2024

אחד המנגנונים החמודים של Vue הוא ה Directives שמאפשרים לכתוב פחות קוד בקוד התבנית ולקחת חלקים של קוד שמשפיעים על ה DOM כדי להשתמש בהם בכמה מקומות. בשביל המשחק בואו ננסה לבנות משהו דומה בריאקט.

1. מה זה Directives

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

<p v-if="seen">Now you see me</p>

המאפיין v-if הוא מאפיין מיוחד מסוג זה. הוא כולל "קוד" שרץ כל פעם שצריכים לרנדר את האלמנט והקוד הזה יכול לשנות את האלמנט או אפילו להוציא אותו לגמרי מהעץ. בדוגמה של v-if אם המשתנה seen הוא אמיתי האלמנט יופיע ואם הוא "שקר" אז האלמנט לא יופיע. הם עושים המון דברים עם Directives ב Vue אבל מה שחשוב לקחת מכאן זה:

  1. דירקטיבס מאפשרים להשפיע על קומפוננטה מבחוץ, בלי קשר לקוד של הקומפוננטה עצמה (לא הייתי צריך לגעת בקוד של p בשביל להיות מסוגל להוסיף לו v-if).

  2. אפשר להוסיף דירקטיבס לכל קומפוננטה.

  3. דירקטיב הוא חלק מקוד הקומפוננטה שלא כתוב בתוכה ומתווסף או מוסר בצורה של הרכבה.

2. חלומות בריאקט

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

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <div>        
        <Text v-if={count % 2 == 0}></Text>        
        <Text v-highlight={true} v-if={count % 2 == 0}></Text>
        <button onClick={() => setCount(c => c+1)}>{count}</button>
      </div>
    </>
  )
}

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

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

const Text = directify((props) => {
  return <p>Hello World</p>
});

כל עוד אני מגדיר קומפוננטות עם הפונקציה directify אפשר יהיה להוסיף להן Directives.

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

export const Directives = {};

export function directify(c) {
  return (props) => {    
    let el = c(props);
    for (const [directiveName, directive] of Object.entries(Directives)) {
      if (directiveName in props) {
        if (el) {
          el = directive(el, props[directiveName]);          
        } else return el;
      }
    }
    return el;
  }
}

הפונקציה מקבלת קומפוננטה, מפעילה אותה ומעבירה את התוצאה ל Directives. ליד הפונקציה יש אובייקט גלובאלי של Directives.

ה Directives עצמן באובייקט הגלובאלי נראות כך:

Directives['v-if'] = (el, value) => {
  if (value) {
    return el;
  } else {
    return false;
  }
}

Directives['v-highlight'] = (el, value) => {
  return cloneElement(el, {
    style: {
      ...(el.props.style),
      backgroundColor: 'yellow',
    }
  })
}

אז כן זה עובד, אבל צריך עוד הרבה עבודה. אפילו בדוגמה הקטנה של ה v-if צריך להבין ש v-if אמור לרוץ לפני שמרנדרים את האלמנט ואם הוא מחזיר false אז לא צריך לרנדר את האלמנט בכלל. מצד שני v-highlight עובד יחסית בסדר. אני לא בטוח איך לדמיין את v-for אבל זה יכול להיות תרגיל מעניין.

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

ספריה מעניינת שהלכה בכיוון הזה היא react-directive. שם הם התמקדו בהוספת תמיכה ב Directives לאלמנט div יחיד ומימוש ה Directives המובנים בלי לבנות תשתית להרחבה ל Directives שלנו.