הוקס עם התניה

09/06/2021

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

function MyProjects () {
  const { data: user } = useSWR('/api/user')
  if (!user) {
    return 'loading...';
  }

  const { data: projects } = useSWR('/api/projects?uid=' + user.id)
  if (!projects) return 'loading...'

  return 'You have ' + projects.length + ' projects'
}

וזה חלום כי ה useSWR השני הוא בהפעלה "מותנית", לפעמים הוא יבוצע ולפעמים לא, וזה אסור לפי חוקי ה Hooks.

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

function MyUser() {
  const { data: user } = useSWR('/api/user')

  if (!user) {
    return 'loading...';
  }

  return (
    <MyProjects user={user} />
  );
}

function MyProjects () {
  const { user } = props;
  const { data: projects } = useSWR('/api/projects?uid=' + user.id)

  if (!projects) return 'loading...'

  return 'You have ' + projects.length + ' projects'
}

נו, עובד אבל מכוער. חלוקה לקומפוננטות לא צריכה להיות מושפעת מאיזה Hooks בחרנו להפעיל.

אופציה שניה וטובה יותר היא מה שעשו ב SWR ומה ששווה לבנות גם ב Hooks שלכם: הכנסת הלוגיקה לתוך ה Hook. ב SWR אפשר לכתוב את אותו הקוד כך:

function MyProjects () {
  const { data: user } = useSWR('/api/user')
  const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id)
  // When passing a function, SWR will use the return
  // value as `key`. If the function throws or returns
  // falsy, SWR will know that some dependencies are not
  // ready. In this case `user.id` throws when `user`
  // isn't loaded.

  if (!projects) return 'loading...'
  return 'You have ' + projects.length + ' projects'
}

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

  if (typeof key === 'function') {
    try {
      key = key()
    } catch (err) {
      // dependencies not ready
      key = ''
    }
  }

ושאר הקוד כתוב כך שידע לטפל במקרה ש key הוא ריק ולהתנהג כאילו אנחנו עדיין בטעינה.

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