ar
Feedback
ToCode

ToCode

الذهاب إلى القناة على Telegram

טיפים קצרים למתכנתים מאת ינון פרק

إظهار المزيد
1 419
المشتركون
-224 ساعات
-17 أيام
-130 أيام
أرشيف المشاركات
ToCode
1 419
# אף אחד אף פעם לא ביקש ללמוד על Higher Order Components לא נעים להגיד אבל יש בטכנולוגיה פיצ'רים שאנשים רוצים ללמוד עליהם ואחרים שלא ממש. אף אחד לא נכנס לקורס ריאקט ומבקש ללמוד איך עובד Higher Order Component או לקורס JavaScript ומבקש ללמוד איך לממש לבד Class System עם פרוטוטייפים. מצד שני כשאנשים נכנסים לקורס על בדיקות הם קודם כל רוצים לשמוע איך להשתמש ב Mock Objects, ב Spies ובכל היכולות הכי מתקדמות של ספריית הבדיקות. בקורס ריאקט אנשים ישאלו אותי לעתים קרובות איך עובדים עם Concurrent Mode וכמובן על useContext ו useMemo. ויש סיבה למשחק הזה. כשאנחנו נכנסים לקוד חדש או לומדים כלי חדש הרבה פעמים אנחנו מרגישים שהפיצ'רים שם מסיבה טובה, וששימוש בפיצ'רים "מסובכים" או "מתקדמים" אומר שאנחנו כותבים קוד טוב יותר. שתוכניות בעולם האמיתי בטח חייבות להשתמש בכל היכולות הכי מתקדמות של הספריה, אחרת היכולות האלה לא היו שם. האמת הפוכה לגמרי: כמעט כל קוד בדיקה שראיתי שהשתמש ב Mock Objects כדי "לדרוס" חלקים במערכת היה אפשר לשכתב בקלות לגירסה יותר אמינה בלי אותם Mock Objects. כמעט אף אחד לא צריך את PureComponent או useMemo כי ריאקט עובד טוב מספיק בלעדיו. מטרת המשחק היא לא לשבץ כמה שיותר פיצ'רים מתקדמים בקוד שלכם, אלא לשבץ את הפיצ'רים הנכונים במקום הנכון.

ToCode
1 419
# זה נראה לך בסדר? כשאתה מסתכל על הקוד הזה, על הפונקציה, על הקובץ. לפני Stack Overflow, לפני שמריצים את הבדיקה, לפני שמסתכלים בלוגים. הקוד הזה נראה לך שצריך לעבוד? נראה לך שיחזיק מעמד לאורך זמן? נראה לך שישרוד את הדרישות המשתנות שצפויות להגיע או את העומס כשהלקוחות יתחילו להגיע? זה נראה לך בסדר? כמו שיש אנשים שיודעים להסתכל על רכב יד שניה ולפני כל הבדיקות יודעים לראות אם משהו מרגיש חשוד ברכב הזה. וכמו שיש עורכי דין שיודעים להסתכל על חוזה ולדבר עם הצדדים ואחרי כמה דקות להבין אם משהו מרגיש פה חשוד או ששווה להתקדם. תכנות, בדומה להנדסה, הוא מקצוע מדעי ומדויק. או שהקוד עושה את מה שהוא צריך לעשות או שלא. או שהוא ישרוד את אתגרי הזמן או שלא. או שיהיה קל לתחזק אותו או שלא. אנחנו לא יודעים מה יקרה בעתיד אבל אנחנו יכולים לנחש. וכשהניחושים שלנו מצליחים אנחנו יכולים להתבסס על ההצלחות ולנחש שוב לפי רעיונות דומים, ולחזור על זה מספיק פעמים עד שנהיה טובים בזה. זה נראה לך בסדר? ועכשיו, חצי שנה אחרי ואחרי כל הבדיקות והעומסים - זה באמת עבד בסדר? ואם לא, על מה תסתכל טוב יותר בפעם הבאה?

ToCode
1 419
# לשבור, בטח לשבור מתכנתים שנתקלים בקוד שלא עובד אוהבים לשאול "אבל מה השתנה פה?", כאילו כל באג הוא מינימום ליל הסדר: אנחנו הופכים את לוג הקומיטים ורצים עם diff-ים בין תיקיות רק בשביל למצוא את הפסיק ששונה בין מה שעבד למצב העגום הנוכחי. אם זה עובד לכם וחוסך לכם זמן הכל טוב. הבעיה מתחילה כשהגישה הזאת לא עובדת, למשל אם ישבת חצי שעה לראות מה שונה ולא מצאת כלום. כשזה קורה אפשר להחליט להמשיך לחפור עם זכוכית מגדלת יותר ויותר גדולה עד שנמצא את הפסיק שבדיוק השתנה, אבל יותר חכם ומהיר יהיה לזרוק לפח את ההיסטוריה ולהתחיל לדבג מאפס, כאילו הקוד אף פעם לא עבד. כאילו מצאתי אותו באיזה תשובה בסטאק אוברפלו ועכשיו צריך להתאים אותו לפרויקט שלי. לא צריך לפחד שהקוד יהיה שונה מאוד מהדבר ההוא שעבד. סיכוי טוב שהדבר ההוא לא באמת עבד כזה טוב ממילא.

ToCode
1 419
# באג ריפורט: אזורי זמן הבאג הקטן הבא מראה איך דקה של חוסר תשומת לב יכולה לעלות בכמה שעות של דיבאגינג, וסיפור שהיה כך היה: ## המערכת המערכת עליה אנחנו מדברים מורכבת משרת מרכזי ומספר סרביסים קטנים יותר לצורך ביצוע משימות קטנות. בשביל להשאיר את הפוסט פשוט נניח שאחד הסרביסים אחראי על משלוח תזכורות לאימייל של משתתפים בפגישות. בגלל ששליחת תזכורת לפגישה קורית הרבה אחרי שמישהו קבע פגישה, התקשורת היא אסינכרונית באמצעות Message Queue. הקוד הבא ב Rails שולח הודעה לסרביס על כל פגישה חדשה שנוצרה או עדכון לפגישה קיימת:
class CreateMeetingReminderJob < ApplicationJob
  def perform(meeting_id)
    meeting = Meeting.find(meeting_id)
    MESSAGING_SERVICE.meetings_queue.publish(
      { id: meeting.id, starts_at: meeting.starts_at }.to_json
    )
  end
end
ובצד של הסרביס הקוד הבא מושך את המידע על הפגישה ויוצר משימה מתוזמנת כדי לשלוח תזכורת:
    channel.consume(queue, async function(msg) {
      const data = JSON.parse(msg.content);
      const numRemoved = await agenda.cancel({ data: { id: data.id }});
      console.log(`canceled previous ${numRemoved} jobs`);
      agenda.schedule(data.starts_at, "send reminder", {
        id: data.id
      });
    }, { noAck: true })
## מה המשתמשים ראו אחרי העברת אחד הסרביסים לשרת אחר פתאום הודעות תזכורת הגיעו בשעות מוזרות או שהפסיקו להגיע בכלל. המשך מחקר הראה שהודעות תמיד מגיעות 5 שעות לפני הזמן שהן צריכות להגיע. מכאן הדרך היתה קצרה לבחינה בלוג של ההודעות שעברו והבנה ששתי המערכות לא עובדות באותו אזור זמן. כשהמערכת המרכזית ביקשה לשלוח תזכורת לפגישה שתתקיים ב 17:00, הסרביס היה בטוח שהפגישה צריכה להתקיים ב 12:00 בצהריים. ## איך זה קרה אזור הזמן ב Node.JS נקבע לפי הגדרות המערכת. אפשר לראות אותו באמצעות הפעלת:
> new Date().toString()
'Sat Apr 10 2021 16:23:39 GMT+0300 (Israel Daylight Time)'
זה אומר שאם מעבירים סרביס Node.JS לשרת שמכוון לאזור זמן אחר, גם הקוד עובר לעבוד באזור הזמן של המערכת החדשה עם כל התוצאות המפתיעות הקשורות לזה. ## מה עושים בעתיד בכתיבת סרביסים ב Node.JS יש לוודא מה אזור הזמן לפיו כל המערכת מכוונת ולדאוג להפעיל את הסרביס באזור הזמן המתאים. ב Node.JS אנחנו צריכים לקבוע את משתנה הסביבה TZ לאזור הזמן המתאים, כלומר הפעלה כזו תעבוד באזור זמן UTC:
$ tz=UTC node reminders_service.js

ToCode
1 419
מבנה לולאה שני שמחזיר ערך ויכול לרוץ בלי להשפיע על העולם החיצון הוא הרכבת רשימות. אני אומר "יכול לרוץ" בגלל שבשפות שאינן פונקציונאליות מתכנתים יצירתיים יצליחו להשתמש במבנה זה כדי ליצור Side Effects או להשפיע על מידע חיצוני. הנקודה כאן היא הפוכה והיא שאפשר להשתמש במבנה זה בצורה פונקציונאלית. אליקסיר היא שפה פונקציונאלית אהובה עליי שבין השאר לא מאפשרת כלל עדכון של מידע אחרי שנוצר, כלומר כל המידע הוא Immutable. התחביר שלהם להרכבת רשימות דווקא משתמש במילה for אבל בצורה מעניינת. הקוד הבא מאתחל ומדפיס מערך באליקסיר מריבועי המספרים מ-0 עד 9:
arr = for i <- 0..9, do: i * i

IO.inspect(arr)
ב Elixir, כמו בשפות פונקציונאליות רבות, המילה for לא מתפקדת כפקודת Flow Control ללא ערך החזר אלא בתור פונקציה לכל דבר: פונקציה שמקבלת פרמטרים ומחזירה ערך שהוא מערך. למרות הכתיב המשונה, אין שום דרך להשתמש ב for כמו בדוגמת ה JavaScript הראשונה ובכל איטרציה להוסיף משהו למערך, פשוט בגלל שבאליקסיר אנחנו לא משנים מערך מרגע שיצרנו אותו. אבל הדבר החשוב הוא מה הקוד כן נותן לנו - בכתיב הרכבת רשימות אין קשר בין "איטרציות" שונות של הלולאה ובעצם אפשר לדמיין שהשפה מפעילה את כל 10 החישובים במקביל. אנחנו לא בונים משהו בהדרגה אלא מריצים 10 חישובים ומשלבים את כל 10 התוצאות. והדבר החשוב, הקוד שבתוך המבנה לא משפיע על המידע שחיצוני לו, כמו ברקורסיה ולא כמו בלולאות ה for וה while המסורתיות. אגב הרכבת רשימות הוא מבנה פופולרי בהרבה שפות כך למשל אותו קוד נראה בפייתון:
arr = [i * i for i in range(10)]
print(arr)
הרכבת רשימות לא חייבת להתעלם מהעולם החיצון ומתכנתים שלא מבינים את העקרונות הפונקציונאליים או שלא מתעניינים בהם יכולים לכתוב את הקוד הבא שהוא חוקי לגמרי בפייתון:
arr = []
[arr.append(i) for i in range(10)]
print(arr)
## פונקציית map מבנה פונקציונאלי פופולרי נוסף הוא הפונקציה map: פונקציית map, בדומה להרכבת רשימות ולרקורסיה, היא מבנה שיש לו ערך החזר ולכן הקוד שבתוכו יכול להתעלם לגמרי מקיומו של עולם חיצוני. אני אומר יכול כי כמו ברקורסיה וכמו בהרכבת רשימות, גם כאן אין בעיה לא לשחק לפי הכללים. הדבר החשוב הוא היכולת והבחירה כן לשחק לפיהם. הנה הקוד ב JavaScript שמייצר מערך של ריבועי המספרים מ-0 עד 9:
const arr = new Array(10).fill(0).map((_, i) => i * i);
הקוד מאוד דומה להרכבת רשימות ולמעשה זה אותו מנגנון פשוט בתחביר אחר. שימו לב איך כל אחד מעשרת החישובים היה יכול להתבצע בנפרד או במקביל ואפילו בתוכנית אחרת, והפונקציה map בסך הכל מחברת את כל התוצאות למערך אחד. ## לסיכום ההבדל בין הגישה הפונקציונאלית לגישה הפרוצדורלית לא קשור למילה מסוימת שמופיעה או לא מופיעה בתוכנית, וגם לא לתחביר שיש או אין בשפה. הגישה הפונקציונאלית תעדיף שימוש במבנים טהורים, כלומר ביטויים שלא משפיעים על מידע גלובאלי חיצוני להם. מבנים תחביריים מסוימים כמו לולאות while ו for לא מסתדרים עם הגישה הזאת בצורה מהותית: בגלל שאין ערך החזר למבנים אלה, הקוד שבתוכם חייב להשפיע על העולם החיצוני ולשנות מידע בזיכרון או ליצור Side Effects. מבנים תחביריים אחרים, אלה שאנחנו מכירים בתור מבנים פונקציונאליים, הם מבנים שכוללים ערך החזר ולכן אפשר להשתמש בהם בלי לשנות מידע חיצוני ובלי ליצור Side Effectsף כלומר בצורה טהורה.

ToCode
1 419
# אז מה בכלל יש לכם נגד לולאות? חבר סיפר השבוע שבתרגיל במכללה בקורס על תכנות פונקציונאלי הם היו צריכים לממש אלגוריתם מסוים "בלי לולאות". התחושה היתה של מימוש בידיים כבולות ולא לגמרי היה ברור מה הבעיה עם לולאות ותכנות פונקציונאלי הבעיה מתחדדת כשנסתכל על שלושת דוגמאות הקוד הבאות:
def double(arr):
    result = []
    for item in arr:
        result.append(item * 2)
    
    return result
def 
ולעומתה:
def mymap(f, arr):
    result = []
    for item in arr:
        result.append(f(item))
    
    return result
    
def double(arr):
    return mymap(lambda x: x * 2, arr)
וזו:
def double(arr):
    return map(lambda x: x * 2, arr)
אין ספק ששלושתן עושות את אותו דבר, אבל שתי הראשונות "משתמשות בלולאות" והשלישית לא. אז מה קורה פה? ומה בעצם הבעיה עליה אנחנו מנסים להתגבר על כלל ה"בלי לולאות?" ## עקרונות תכנות פונקציונאלי ביקור בויקיפדיה מראה לנו את העקרונות הבאים של תכנות פונקציונאלי: 1. פונקציות הן First Class Citizens 2. העדפת פונקציות טהורות וביטויים טהורים 3. רקורסיה 4. הימנעות מ Side Effects ו Referential transparency בהקשר של לולאות עיקרון מספר 2 הוא הבולט ביותר: פונקציה או ביטוי נקראים טהורים אם אנחנו יכולים להפעיל אותם כמה פעמים, או לשנות את סדר ההפעלה בין ביטויים, או להפעיל מספר ביטויים טהורים במקביל, ותמיד לקבל את אותה תוצאה. נעבור לבחון מבני לולאות מרכזיים בשפות תכנות פופולריות ולראות איזה מהם מסתדרים ואיזה לא מסתדרים עם הרעיון הפונקצונאלי. ## לולאת for הלולאה הראשונה היא לולאת for שיכולה להופיע עם משתנה רץ או בתור לולאת for ... in. דוגמת הקוד הבאה מ JavaScript ממלאת מערך במספרים מ-0 עד 9 כולל:
const arr = [];
for (let i=0; i < 10; i++) {
    arr.push(i);
}
הקוד לא מתאים לגישה הפונקציונאלית בגלל שהביטוי בתוך ה for אינו ביטוי טהור: אם אריץ את הפקודה arr.push כמה פעמים תוצאת התוכנית כולה תהיה שונה. כשמסתכלים על התמונה הגדולה שמים לב שהבעיה היא מהותית ללולאות for. בלולאת for אין ערך החזר ולכן כל מה שיקרה בתוך גוף הלולאה ישפיע על מידע חיצוני או ייצור Side Effect. ב Python המצב עוד יותר מוזר כי משתנה שמוגדר כחלק מלולאת for נשאר פעיל בתוכנית גם אחרי הלולאה, כלומר הקוד הבא מדפיס 9:
for i in range(10): pass

print(i)
הלולאה כשלעצמה לא "מחזירה" ערך אבל היא משפיעה על העולם החיצון, וזו כבר הפרה בוטה של הגישה הפונקציונאלית. ## לולאת while גם לולאת while תקועה עם אותה בעיה: מאחר ואין לה ערך החזר, הדרך היחידה של לולאת while להיות בעלת משמעות בתוכנית היא להשפיע על דברים שנמצאים מחוץ ללולאה, באמצעות יצירת Side Effects או גישה למידע חיצוני. ## רקורסיה אם כל מבנה של לולאה בלי ערך החזר אינו מתאים לגישה הפונקציונאלית, איך בכל זאת אפשר לכתוב קוד פונקציונאלי שיעשה מספר פעולות שאינו ידוע מראש בזמן כתיבת הקוד? המבנה הראשון שעומד בקריטריון הפונקציונאלי ופותר את הבעיה יהיה הרקורסיה. רקורסיה היא פונקציה שלפעמים תחזיר ערך ולפעמים תפעיל את עצמה כדי להמשיך את החישוב. לדוגמה אותה משימה שמילאה מערך ב-10 מספרים תיראה כך בגישה הרקורסיבית:
def create_array(count):
    if count == 0:
        return []

    return [*create_array(count - 1), count - 1]
        
arr = create_array(10)
print(arr)
הפעם כל ביטוי בתוכנית משפיע אך ורק על עצמו. אני יכול לדמיין שאני רץ ב Debugger על התוכנית הייתי יכול להריץ כל שורה פה כמה פעמים שאני רוצה בלי לפגוע ברצף ובמשמעות של התוכנית. אפשר לתאר את זרימת התוכנית בתור אוסף של פונקציות שמופעלות אחת על השניה וכל אחת מחזירה ערך לפונקציה שקראה לה. ה"לולאה", שהתאפשרה רק בזכות היכולת להגדיר פונקציה והיכולת של פונקציה לקרוא לעצמה, הפכה ללולאה שמחזירה ערך כי הפונקציה create_array מחזירה ערך. למרות שאנחנו יכולים להשתמש ברקורסיה בצורה פונקציונאלית חשוב לזכור שאין שום דבר במבנה שמכריח אותנו לעשות את זה. הקוד הבא חוקי לגמרי בפייתון, משתמש ברקורסיה ובכלל לא משחק לפי הכללים של תכנות פונקציונאלי:
arr = []

def init(count=0):
    if count >= 10:
        return
        
    arr.push(count)
    init(count + 1)
    
init()
print(arr)
## הרכבת רשימות

ToCode
1 419
# מהירות כפולה מצאתי לאחרונה פודקסט בשם Russian with Max. מקס הקליט מעל 100 פרקים, כל פרק באורך כ 50 דקות ברוסית מספיק איטית ופשוטה כדי שאפילו אני אצליח להבין את הרעיון המרכזי מהפרק. בינתיים הספקתי להקשיב לשני פרקים, ואני יודע מניסיון ומהגיון בריא שאם אקשיב לפרק ביום ב-3 חודשים הקרובים זה ישפר משמעותית את ההבנה שלי ברוסית. ואז התחלתי לחשוב- אולי אני יכול לקצר זמנים? מה אם אקשיב ל-3 פרקים ביום? או אכוון את המהירות לכפול 2 ואקשיב ל-6 פרקים ביום? אם אצליח לסיים להקשיב לכל 100 הפרקים של הפודקסט בשלושה ימים, האם אוכל לקבל את אותה דחיפה קדימה, כמו בתוכנית הפרק ביום המקורית שלי? כמובן שלא. שני דברים חשובים הולכים לאיבוד בגירסת הלמידה בהילוך מהיר: 1. הראשון הוא הזמן שהמוח מעבד את מה שנכנס בזמן ההאזנה עצמה. בלי קשר לקצב הדיבור, להקדיש 50 דקות בתוך פעילות נותן למוח יותר זמן עיבוד מאשר להקדיש 25 דקות לאותה פעילות, אפילו אם המילים שנשמעו הן אותן מילים. 2. והשני, ואולי יותר חשוב, הוא הזמן שהמוח מעבד את מה שנכנס בין הפרקים. אז בדוגמה קונקרטית אני יושב ומקשיב לפרק על מוזיקת רוק בברית המועצות ומקס מספר איך פעם צרבו תקליטים על צילומי רנטגן ישנים כי אסור היה להכניס תקליטים לשם, וכשנגמר הפרק אחרי עשרות פעמים שהוא חזר על המילה הרוסית ל"תקליט" הייתי בטוח שזהו, שאת המילה הזאת אני לא אשכח לעולם. נו, לא תופתעו לשמוע ששלוש שעות אחרי זה פתאום ניסיתי להיזכר במילה ל"תקליט" והיא לא היתה שם. אנחנו בונים את הידע בשכבות ובצורה אסינכרונית. שומעים קצת, מעבדים קצת, ואז עוברים למשהו אחר וחוזרים. בשביל לחזק את הידע - בכל תחום - כדאי לתת למוח זמן בתוך הפעילות וזמן אסינכרוני בין הפעילויות. לכן אין טעם להתמקד בכמה מהר אני מסיים לצפות בכל סרטי הוידאו או תוך כמה זמן אני מצליח לסיים אוסף תרגילים מסוים. הרבה יותר מעניין ואפקטיבי לבנות תוכנית לימודים שמורכבת מפעילויות לימודיות יומיות, מגוונות, ועם הפסקות ביניהן. תנו למוח לבנות את השכבות בקצב שלו והבניין שאנחנו בונים יישאר יציב לאורך זמן.

ToCode
1 419
# השעה הראשונה דרך אחת לגשת ללמוד נושא חדש מאפס היא להסתכל עליו מבחוץ: לשמוע מה זה ריאקט, מי צריך אותו, למה התחילו לפתח אותו, מה בא לפניו. אולי אפילו לראות מישהו כותב קומפוננטת ריאקט ומסביר על זה. דרך שניה שלפחות עבורי עובדת הרבה יותר טוב היא דווקא להתחיל מבפנים: קודם לכתוב תוכנית ראשונה אפילו שאני לא מבין כלום ושכל הקוד הוא העתק-הדבק. אחריה להמשיך לכתוב תוכנית נוספת כל יום ומפה להתחיל לשלב את ההרצאות והספרים. יש משהו בלהיות בתוך הפרוסס הזה של כתיבה שנותן את ה Context לכל המסביב. וברגע שיש Context ומוטיבציה כל השאר רץ מעצמו.

ToCode
1 419
# היום גיליתי: brew ב linux עלול לשבור הכל הומברו היה מנהל החבילות האהוב עליי במק ושמחתי לראות שהוא עשה הגירה גם ללינוקס. ההתקנה הלא מוצלחת היום גרמה לי לחשוב שאולי שמחתי לפני הזמן, ומעשה שהיה כך היה: חלק מהחבילות ב brew דורשות קומפילציה. ברו בשמחה מתקין את כל התלויות ומקמפל, אבל מסתבר שכחלק מהתהליך הזה הוא מתקין גם חלק מכלי הבניה עצמם וספציפית את pkg-config. הפקודה pkg-config למי שלא מכיר היא כלי שמחזיר מידע על ספריות שמותקנות לנו על המכונה ואיפה הן מותקנות. ומה הבעיה עם זה שהומברו יתקין את pkg-config אתם שואלים? נו זה ברור, ה pkg-config שהומברו מתקין יכול להחזיר מידע רק על חבילות שהומברו התקין. אז עכשיו אנחנו יכולים לנסות לבנות תוכנה שצריכה איזו חבילה (נגיד libdbus-1-dev), ואפילו שהחבילה מותקנת דרך apt-get אי אפשר יהיה לבנות את התוכנה שלנו כי הבניה מפעילה את pkg-config שמחפש רק בספריות שהותקנו דרך הומברו. אחרי שמבינים מה לחפש גם הפיתרון די פשוט ומופיע ב Issue הזה. כל מה שצריך זה להסיר את pkg-config מהומברו וכך חוזרים לגירסה של המערכת:
brew unlink pkg-config
נ.ב. המפתח לגילוי שמשהו לא בסדר הוא פשוט הפקודה which שמספרת לנו מאיזו תיקיה הופעלה תוכנית מסוימת. which pkg-config סיפר לי שאני משתמש ב pkg-config של הומברו במקום בזה של המערכת ומשם ההמשך היה קל. שימו לב כשאתם בלינוקס תמיד לוודא שאתם מריצים את מה שאתם חושבים שאתם מריצים.

ToCode
1 419
# לימוד לפי תרחישים התרחיש הפרקטי הוא הצעד הראשון בזה אין ספק. אם אני רוצה ללמוד להגיד "בוקר טוב" באנגלית אני מתחיל בגוגל ומוצא את ה Good Morning יחסית מהר. אבל פה הכיף רק מתחיל, כי עכשיו נמשיך ונשאל: 1. איזה עוד משפטים אפשר להגיד עם good ? 2. ואיזה עוד משפטים יש עם המילה morning ? 3. והאם יש דברים אחרים שאפשר להגיד לחברים בבוקר? 4. ומה אומרים בצהריים? ובערב? ולפני השינה? 5. ומה מקור המילה good ? באיזה עוד שפות היא מופיעה? 6. ומה לגבי morning? ומה זה בכלל morrow? ואיך זה קשור ל tomorrow? הפרקטיקה פותחת את הדלת. התיאוריה זה האוצר שמוצאים בפנים.

ToCode
1 419
# נראה כמו קומפוננטה ההרגל הבא היה נפוץ בעולם הקלאסים אבל אנחנו עדיין רואים גם בעולם הקומפוננטות הפונקציונאליות ונשאר מסוכן באותה המידה. אני מדבר על פונקציות שמחזירות JSX אבל אינן קומפוננטות, לדוגמה:
function createBox(props) {
    const style = {
        display: 'inline-block',
        border: '1px solid',
        width: '80px',
        height: '80px',
        verticalAlign: 'top',
        ...props.style,
    };
    
    return (
        <div style={style} {...props} />
    );
}

function App() {
    return (
        <div>
            {_.range(10).map(() => createBox())}
        </div>
    );
}
הפונקציה App היא אכן קומפוננטה ומחזירה מערך של 10 div-ים עם עיצוב מתאים. הפונקציה createDiv יותר בעייתית: היא אינה קומפוננטה אבל היא נראית קצת כמו והיא מסבכת אותנו כשצריך לעשות Refactor. אם בעתיד נצטרך לשמור סטייט בתוך הקופסאות האלה ונרצה להוסיף קריאה ל useState לתוך הפונקציה, אז הסטייט יישמר בקומפוננטה שקוראת לפונקציה כלומר ב App. בגלל שהפונקציה לא נראית כמו Custom Hook קל להתבלבל ולהפעיל אותה מתוך תנאי או לולאה מה שיגרום לתוצאות לא צפויות. חלק מהיופי במעבר לקומפוננטות פונקציונאליות ול Hooks הוא שהקוד של קומפוננטה פונקציונאלית הוא לא יותר מסובך מקוד של פונקציה פשוטה. הגירסה הבאה של אותו הקוד תהיה הרבה יותר קלה לתחזוקה:
function Box(props) {
    const style = {
        display: 'inline-block',
        border: '1px solid',
        width: '80px',
        height: '80px',
        verticalAlign: 'top',
        ...props.style,
    };
    
    return (
        <div style={style} {...props} />
    );
}

function App() {
    return (
        <div>
            {_.range(10).map(() => <Box />)}
        </div>
    );
}