en
Feedback
ToCode

ToCode

Open in Telegram

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

Show more
1 420
Subscribers
+124 hours
+17 days
-430 days
Posts Archive
ToCode
1 420
# תיעדוף אחד הקשיים של עבודה בעולם המודרני הוא שאין לה סוף. אף אחד לא חוזר הביתה עם הרגשה של "איזה כיף סיימתי את כל העבודה ועכשיו אפשר לנוח". תמיד יש עוד מה לעשות וזה כך בכוונה. והקושי הוא שעבודה אינסופית מכריחה אותנו לתעדף, לבחור כל יום איזה בעיות לפתור ואיזה בעיות להשאיר בצד. ותיעדוף לא רק שאינו הצד החזק שלנו, אלא גם שכל השנים בבית ספר זה משהו שלא תרגלנו (בשביל ציון גבוה בבית ספר היה צריך לפתור את כל המבחן, לא לבחור רק את החלקים החשובים ממנו). ברירת המחדל, או התיעדוף הנאיבי, הוא לפתור קודם כל את הבעיות הדחופות, אחר כך את הבעיות הקלות, ובסוף את הבעיות המסובכות. וזה יוצר שתי בעיות: 1. עם הזמן יותר ויותר בעיות הופכות "דחופות", פשוט בגלל שזאת הדרך היחידה של בעיות (וש האנשים שמייצרים את הבעיות) לקבל יחס. 2. עם הזמן בעיות מסובכות צוברות "ריבית" והופכות ליותר מסובכות. וכך נוצר מעגל אכזרי של כיבוי שריפות - ככל שאנחנו מכבים יותר שריפות כך נוצרות שריפות יותר גדולות, שדורשות יותר יחס וזמן, מה שלא משאיר זמן להתמודד עם הבעיות המסובכות, שיהפכו בתורן לבעיות הרבה יותר גדולות ודחופות. הפיתרון היחיד, וגם הטוב ביותר לאנשים שרוצים להתקדם ולשמור על איזון בין החיים לעבודה, הוא תיעדוף טוב יותר. תיעדוף שבו אנחנו מטפלים בבעיות החשובות היום כדי שהן לא יתהפכו עלינו בעוד חודש או שנה. דברים יפלו? בוודאי. בעיות דחופות לא יקבלו מענה? אין ספק. אבל האמת שזה כבר קורה בין אם נרצה ובין אם לא. ביממה יש רק 24 שעות, ולא משנה כמה תעבדו קשה עדיין תפספסו הרבה בעיות דחופות. לא חייבים לפתור את כל הבעיות. חשוב לפתור את החשובות.

ToCode
1 420
# מדריך וידאו חדש: פיתוח יישומי Full Stack React עם Next.JS 14 לפני שבוע יצאה רשמית גירסה 14 של next.js. שתי הגירסאות האחרונות - 13 ו 14, התמקדו ביצירת תשתית לפיתוח יישומי Full Stack ולאינטגרציה בין קוד צד שרת לקוד צד לקוח. העבודה התמקדה ב-2 פיצ'רים מרכזיים: חלוקה לקומפוננטות צד-שרת וקומפוננטות צד-לקוח, ויצירה אוטומטית של API באמצעות מנגנון Actions. שני המנגנונים מבוססים על יכולות תשתית של ריאקט ומרחיבים יכולות אלה. הרעיון החדש הראשון שנכנס עוד ב next 13 הוא קומפוננטות צד שרת, או יותר נכון קומפוננטות צד-שרת בלבד. אלה קומפוננטות ריאקט שעוברות Server Side Rendering אבל לא עוברות Hydration בדפדפן כך שאין צורך לשלוח את ה JavaScript שלהן לדפדפן. קומפוננטות אלה מוגדרות על ידי פונקציות אסינכרוניות ויכולות להשתמש בכל היכולות של קוד צד שרת, כלומר משהו בסגנון הזה:
export default () => {
    const users = await listUsers();
    return (
        <p>{users.length} connected users</p>
    );
};
כאשר listUsers יכולה להיות פונקציה אסינכרונית שמתחברת לבסיס הנתונים ומושכת ממנו מידע. תוצאה של קומפוננטה כזאת היא פשוט HTML ללא JavaScript שמציג את התוצאה. המנגנון השני שנכנס ב next 14 אפילו יותר מעניין - הוא מאפשר לקומפוננטת צד-לקוח "להפעיל" פונקציית צד שרת מתוך קוד טיפול באירוע, כלומר משהו בסגנון הזה:
export default () => {
    const login = async () => {
        const isConnected = await checkUser();
    };
    
    return (
        <button onClick={login}>Login</button>
    );
};
כאשר checkUser היא פונקציית צד שרת שיכולה להתחבר לבסיס הנתונים ולמשוך ממנו מידע. פיתוח אפליקציית React עם Next.JS היום נותן לנו אפשרות להתמקד בקוד המערכת עצמה, בלי שנצטרך לבנות שכבות חיבורים ולהגדיר API לכל פעולה. המערכת גם כוללת מנגנונים מובנים לטיפול בשגיאות והצגת סטטוס כך שהכל מחובר ועובד ישר מהקופסא. למידע נוסף על המנגנונים ועל פיתוח פרויקט TypeScript עם Next.JS שכולל קוד צד-שרת וצד-לקוח, מוזמנים לצפות במדריך וידאו חדש שהעליתי. במדריך אני בונה פרויקט חדש מאפס הכולל קומפוננטות צד-שרת, קומפוננטות צד-לקוח ואת החיבור ביניהן בעזרת Actions. המדריך סגור למנויי האתר בלבד אז אם אתם כבר מנויים פשוט נכנסים ללינק, ואם לא אז אולי זאת הזדמנות טובה להצטרף (וגם זה בלחיצה על אותו לינק): https://www.tocode.co.il/boosters/next14-fullstack-react. כמו כן אם אתם מנויים לאתר ויש נושאים שהייתם שמחים לשמוע עליהם במדריכי וידאו נוספים אל תתביישו להשאיר הודעה או לשלוח לי מייל.

ToCode
1 420
# רגעים של הארה בשביל רגע של הארה לא מספיק להיחשף למידע חדש או להשתכנע שמשהו חדש הוא נכון. אין שום הארה בלקרוא עוד עובדה על דינוזאורים באיזה ספר טבע, או בלגלות איך לכתוב פונקציה אנונימית בסקאלה. זה פשוט עוד פריט מידע שמצטרף לאינסוף פריטי מידע אחרים בראש. בשביל רגע של הארה דרוש גם מתח ודרושה התנגשות. התנגשות בין המידע החדש למשהו קיים. גילוי שתוצאתו ויתור על הישן והחלפתו בידע מעמיק יותר. חשבו על הארות כמו "אהה - עכשיו אני מבין למה צריך Virtual DOM" או "אהה - עכשיו אני מבין איך אפשר לחיות בלעדיו" ובעיקר "עכשיו אני מבין למה מה שניסיתי לעשות לא יכול לעבוד". והארות דורשות מאמץ. הן דורשות מאיתנו לחפש להוכיח מחדש כל הזמן את האמיתות הישנות, כל הזמן לחשוף דברים שהאמנו בהם אפילו בצורה לא מודעת, ולהיות מוכנים להיפרד מאותן אמיתות כשמידע חדש מגיע. "בואי ננסה, אולי אני טועה" זו תשובה מצוינת כשאנשים מציעים רעיונות מוזרים (כמובן כל עוד אפשר לגדר את הסיכון).

ToCode
1 420
# לגזור ולשמור: איך להוריד את Google Chrome ו chromedriver מתאים מתוך Dockerfile ב 2023 כרום עובד ממש בסדר במצב "ללא ראש" מקונטיינר דוקר, וזה נוח כשרוצים להריץ בדיקות אוטומטיות על מחשב בלי ממשק גרפי. יש גם לא מעט טמפלייטים של Dockerfile ברשת שמראים איך להתקין את כרום ואת ה chromedriver כדי שאפשר יהיה להפעיל אותו מתוך סלניום. הבעיה? רובם ישנים ולא לוקחים בחשבון שינוי שגוגל עשתה לא מזמן ב URL-ים לכרומדרייבר. ומאחר ואחת המטרות של הבלוג הזה היא לעזור לי לשמור דברים חשובים כדי שיהיה לי קל למצוא אותם אחרי (וכן גם לשתף אתכם באותם הדברים כדי שלא תצטרכו לבזבז את הזמן שאני בזבזתי) זה ה Dockerfile שעבד לי נכון לנובמבר 2023 כדי להתקין ולהריץ כרום במצב ללא ראש על אובונטו:
FROM ubuntu:23.04

WORKDIR /app

RUN apt-get update &&\
    apt-get install -y xvfb gnupg wget curl unzip jq ca-certificates --no-install-recommends &&\
    wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - &&\
    echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list &&\
    apt-get update -y && \
    apt-get install -y google-chrome-stable && \
    export CHROMEDRIVER_URL=$(curl https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json|jq -r '.channels.Stable.downloads.chromedriver | .[] | select(.platform == "linux64").url') &&\
    wget -q --continue -P /chromedriver $CHROMEDRIVER_URL &&\
    unzip /chromedriver/chromedriver* -d /chromedriver

# make the chromedriver executable and move it to default selenium path.
RUN chmod +x /chromedriver/chromedriver-linux64/chromedriver
RUN mv /chromedriver/chromedriver-linux64/chromedriver /usr/bin/chromedriver


RUN groupadd -r user && useradd -m -r -g user user && chown -R user /app
USER user

EXPOSE 9222

# default entry point.
CMD ["/usr/bin/google-chrome", "--headless", "--disable-gpu", "--disable-software-rasterizer", "--remote-debugging-port=9222", "--remote-debugging-address=0.0.0.0", "https://www.google.com"]
## end base stage.
אחרי שבונים אותו נפעיל עם חיבור פורט 9222 כדי לאפשר דיבג ממכונה אחרת:
$ docker run --privileged --rm -p 9222:9222 headless-chrome
ולבסוף מהמחשב המקומי שלי אני פותח מנהרה כדי שאפשר יהיה לדבג את הכרום המרוחק:
ssh -L 9222:localhost:9222 user@host-running-the-container.com
כמה נקודות חשובות- 1. האופציה remote-debugging-address בשורת הפקודה מאפשרת להתחבר לקונטיינר מתוך המכונה שמריצה אותו כדי לדבג מרחוק. 2. כרום אוהב להתלונן שיש לו מצב headless חדש וצריך להפעיל אותו עם האופציה headless=new. הם לא מספרים שאם תעשו את זה אז remote-debugging-adress יפסיק לעבוד ואי אפשר יהיה לדבג מרחוק. 3. לגוגל היה פעם מנגנון די מסורבל כדי לגלות איזה גירסה של chromedriver צריך להוריד. הם החליפו את זה ב endpoint אחד שמחזיר JSON עם כל המידע. ואם יש לכם עוד טיפים על הרצת כרום ללא ראש מתוך דוקר מוזמנים בחום להשאיר כאן בתגובות.

ToCode
1 420
# היום למדתי: CSS Nesting כבר עובד בדפדפן איפשהו בעבר הרחוק מישהו סיפר לי על Less ו Sass ואיך שווה להשתמש בהם כי אפשר לכתוב סלקטור בתוך סלקטור וזה היה מאוד מרגש בשעתו. עם הזמן נפרדנו מ Less ולמדנו לקבל את Sass ונכון שלימים גם הוא הוחלף ב CSS In JS וב Tailwind אבל עדיין יש משהו קסום בלכתוב CSS ויש עדיין לא מעט פרויקטים עם קבצי css. עכשיו נכון Sass כולל המון המון פיצ'רים וקינון סלקטורים הוא רק אחד מהם, אבל עדיין קשה לדמיין ש Sass בכלל היה נולד אם סלקטורים מקוננים ומשתנים היו עובדים ב 2006. אבל אנחנו כבר לא ב 2006 ואפילו לא ב 2016. משתנים ב CSS כבר עובדים מזמן והיום במקרה גיליתי שגם סלקטורים מקוננים כבר עובדים ברוב הדפדפנים המודרניים. איך זה נראה? נו כמעט כמו שזה נראה ב Sass, רק בלי הפריקומפיילר. קוד HTML:
<form>
  <label for="name">Name:
    <input type="text" id="name" />
  </label>
  <label for="email">email:</label>
  <input type="text" id="email" />
</form>
קוד CSS:
input {
  /* styles for input not in a label  */
  border: tomato 2px solid;
}
label {
  /* styles for label */
  font-family: system-ui;
  font-size: 1.25rem;
  input {
    /* styles for input in a label  */
    border: blue 2px dashed;
  }
}
ולייב קודפן: <iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/ynonp/embed/abXZbvo?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/ynonp/pen/abXZbvo"> Untitled</a> by Ynon Perek (<a href="https://codepen.io/ynonp">@ynonp</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> https://codepen.io/ynonp/pen/abXZbvo לכל הפרטים וההבדלים מול Sass שווה להעיף מבט בתיעוד בקישור: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting

ToCode
1 420
# טיפ JavaScript - ניסוי עם ערך ברירת מחדל בעבודה עם קוד אסינכרוני ב JavaScript טיפול בשגיאות הוא הכרחי, הבעיה שהמנגנון הקיים בשפה לטיפול בשגיאות בקוד אסינכרוני קצת מסורבל. נתבונן בקוד הבא:
async function sleepOrDie(ms, result) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve(result);
      } else {
        reject();
      }
    }, ms)
  })
}

async function handleClick() {
  const value = await sleepOrDie(50, 10);
  alert(value);
}

document.querySelector('button').addEventListener('click', handleClick);
אני יודע לא מתוחכם במיוחד אבל מספיק בשביל הדגמה. אם הפונקציה האסינכרונית מצליחה אז מקבלים ערך, אבל אם היא נכשלת נזרק Exception. זה עובד אם הטיפול שלנו בשגיאות הוא ב flow אחר ואז catch יוכל להפעיל את אותו flow. אבל מה אם אני פשוט רוצה לתת ל value ערך ברירת מחדל אם היתה שגיאה? למשל אם אני מנסה לקרוא ערך מבסיס נתונים או מהגדרות משתמש ואם הערך לא שם הכל בסדר והקוד ממשיך עם אותה ברירת מחדל. במצב כזה try/catch יכול להיראות מסורבל:
async function handleClick() {
  let value = 0;
  try {
    value = await sleepOrDie(50, 10);
  } catch (err) {
    
  }
  alert(value);
}
מבחינת נכונות הקוד טוב יותר, אבל מבחינת התחביר זה פשוט נראה רע: ה const הפך ל let לגמרי בלי סיבה, ואני תקוע עם בלוק catch ריק. טכניקה טובה כדי להסתיר קוד מכוער היא לזרוק אותו לפונקציה. ריפקטורינג קצר ונקבל:
async function tryOr(fn, args, defaultValue) {
  try {
    return await fn.apply(null, args);
  } catch (err) {
    return defaultValue;
  } 
}

async function handleClick() {
  const value = await tryOr(sleepOrDie, [50, 10], 0);
  alert(value);
}
ומיטיבי לכת יכולים להוסיף את הפונקציה הזו לפרוטוטייפ של פונקציה ב JavaScript כדי שתהיה זמינה לנו בכל מקום בתוכנית:
Function.prototype.tryOr = async function (args, defaultValue) {
  try {
    return await this.apply(null, args);
  } catch (err) {
    return defaultValue;
  }
}


async function handleClick() {
  const value = await sleepOrDie.tryOr([50, 10], 0);
  alert(value);
}

ToCode
1 420
# הצצה לעתיד: פעולות צד שרת בריאקט ו Next.js 14 לפני כמה ימים כתבתי כאן על פעולות צד-לקוח, פיצ'ר חמוד של ריאקט שהולך לחסוך לנו כמה הקלקות בכתיבת קוד שמטפל בטפסים. החלק השני שלו, שנקרא Server Actions, כבר הרבה יותר מהפכני ודורש תמיכה מפריימוורק בצד שרת. בזכות Next.JS 14 שיצא עכשיו נוכל לראות איך זה עובד ולהבין את הכיוון של ריאקט בתור Full Stack Framework. ## הסיפור מאחורי פעולות צד-שרת הן מנגנון שבו קומפוננטה ריאקטית מגישה טופס לשרת, וכל תהליך הגשת הטופס וקבלת התשובה מנוהל על ידי הפריימוורק. בכתיבת פעולות צד שרת אנחנו צריכים לכתוב רק שני דברים: 1. את קוד הקומפוננטה, כולל שימוש ב Hook מיוחד כדי להציג ממשק שונה למצב טעינה. 2. את הפונקציה בשרת (ב node.js) שמטפלת בהגשת הטופס. כל הנושא של שליחת הודעה ב http, סריאליזציה של הפרמטרים, המתנה ופיענוח תשובות מבוצע בצורה אוטומטית על ידי הפריימוורק. ## דוגמת קוד קוד? ברור. בפרויקט next.js אנחנו צריכים בסך הכל שני קבצים. הקובץ page.tsx עם הקומפוננטה של העמוד והקובץ server_actions.ts שמגדיר את הקוד שמטפל בטופס בצד השרת. בקובץ server_actions.ts אני כותב:
'use server'
const process = require('node:process');

export async function createItem(data: FormData) {
  console.log(`Client sent form: ${JSON.stringify(Object.fromEntries(data))}`);
  console.log(`I'm a server action pid = ${process.pid}`);
  await new Promise((resolve, reject) => {
    setTimeout(resolve, 3000)
  });
  return `${process.pid}/${data.get('name')}`
}
התחילית 'use server' אומרת ל next.js שאת הפונקציה הזאת צריך להריץ בצד שרת. הפונקציה עצמה משתמשת בדברים של צד שרת לדוגמה process.pid, ומקבלת בתור קלט משהו מסוג FormData - אלה הערכים בטופס שיגיעו מהלקוח. בקובץ page.tsx יהיה לי קוד קצת יותר ארוך שמגדיר שתי קומפוננטות. הקומפוננטה הראשונה היא הטופס:
export default function Page() {
  const [id, setId] = useState('');

  async function onSubmit(formData: FormData) {
    const nextId = await createItem(formData);
    setId(nextId);
  }
  return (
    <form action={onSubmit}>
      <Header />
      {id != '' && <p>Ready. Id = {id}</p>}
      <input type="text" name="name" />
      <button type="submit">Submit</button>
    </form>
  );
}
אני מגדיר action לטופס ובתוך הפונקציה פשוט מפעיל את הפונקציה מ server_actions.ts ושומר את התשובה למשתנה. מאחורי הקלעים ריאקט ישלח את הטופס לשרת, הפונקציה תרוץ בשרת ותחזיר את התוצאה בתשובה לבקשה, הכל דרך הרשת ועם סריאליזציה אוטומטית. בשביל להציג מסך טעינה כשהמידע נשלח לשרת בניתי את הקומפוננטה Header עם הקוד הבא:
function Header() {
  const status = useFormStatus()

  if (status.pending) {
    return <p>Loading...</p>
  }
}
הפונקציה useFormStatus הגיעה מ react-dom (רק בגירסאות חדשות של ריאקט), ומחזירה אוביקט עם מספר שדות שמספר מה מצב הגשת הטופס האחרון שהוגש. שורה תחתונה מנגנון Server Actions הופך את ריאקט להרבה יותר פריימוורק מלא לפיתוח Full Stack מאשר ספריה קטנה וגמישה לפיתוח שכבת התצוגה. יהיה מעניין לראות את ההשלכות של זה על האקוסיסטם. מצד אחד אין ספק שהרבה יותר קל לבנות פרויקט Full Stack מאפס כשכל הכלים מנגנים יפה יחד, אבל מצד שני הרבה אנשים (כולל אני) יתגעגעו לגמישות המינימליסטית של ריאקט הישן והטוב. נמשיך לעקוב.

ToCode
1 420
# היום למדתי: הפלוס הקטן בסקאלה בואו נכתוב תוכנית סקאלה שמחזיקה "דברים" שיכולים להישמר לשרת מרוחק. אפשר לדמיין קלאס של משתמש שיראה כך:
case class User(name: String)
ואולי קלאס של מסמך:
class Document(title: String, content: String)
בשביל לשמור אותם לשרת מרוחק אני צריך להוסיף לכל אחד מהם url, ובשביל לא לשנות את הקלאסים אני יכול להשתמש ב Generic וליצור גירסה "מרוחקת" שלהם, כלומר:
case class Remote[S](value: S, url: String)
אז עכשיו משהו שהוא Remote[User] יחזיק שדה בשם value מסוג User וגם שדה בשם url מסוג String. ואולי נרצה לכתוב פונקציה כללית שיכולה לקבל משהו שהוא Remote ולשמור את הערך מ value לשרת המרוחק באמצעות HTTP Post. היינו עשויים לחשוב שהחתימה של פונקציה כזו תיראה כך:
def save(what: Remote[Any]): Unit =
  println(s"Saving ...")
אבל אם ננסה ליצור משתמש ולשמור אותו נקבל שגיאת קומפילציה. כלומר הקוד הבא:
@main def main()=
  val a = Remote[User](value = User("ynon"), url = "/admin")
  save(a)
לא מצליח להתקמפל בגלל השגיאה:
Found:    (a : Remote[User])
Required: Remote[Any]
  save(a)
מה קרה כאן? הפונקציה מצפה לקבל משהו מטיפוס Remote[Any]. נכון ש User הוא סוג של Any ואם היתה לנו פונקציה שמקבלת Any לא היתה בעיה להעביר לה משהו מסוג User, אבל באופן רגיל Remote[User] איננו משהו מסוג Remote[Any]. פיתרון? למעשה שניים. הכי קל להגדיר את הפונקציה בתור פונקציה גנרית ולא להתעסק עם ירושה כלומר נשנה את save ל:
def save[S](what: Remote[S]): Unit =
  println(s"Saving ...${what.value} to ${what.url}")
והכל מתקמפל ועובד. הפיתרון השני והוא הסיפור של הפוסט הזה נועד למקרים בהם לא הצלחתם להפוך את הפונקציה לגנרית מכל מיני סיבות או שיש לכם Use Case טיפה שונה, ואתם באמת רוצים להגיד משהו אחר על הטיפוס Remote[S] - הייתם רוצים להגיד שאם יש לכם פונקציה שמצפה לקבל Remote[S] לא משנה מה זה S, ויש לכם משהו אחר נקרא לו T שיורש מ S, אז Remote[T] יירש מ Remote[S]. ובשביל להגיד את זה בסקאלה אפשר להוסיף סימן + לפני ה S בהגדרת הטיפוס הגנרי. זה נראה כך:
case class Remote[+S](value: S, url: String)
case class User(name: String)
class Document(title: String, content: String)

def save(what: Remote[Any]): Unit =
  println(s"Saving ...${what.value} to ${what.url}")

@main def main()=
  val x: Any = User("ynon")
  val a = Remote[User](value = User("ynon"), url = "/admin")
  save(a)
וגם הגירסה הזו מתקמפלת ועובדת. ולינק אחרון לסקרנים שרוצים לקרוא עוד על המנגנון הזה בתיעוד של סקאלה, על המינוס שאפשר לרשום במקום הפלוס, ועל השמות באנגלית של שניהם יהיה תיעוד Variances מהסיור בסקאלה: https://docs.scala-lang.org/tour/variances.html

ToCode
1 420
# ריאקט מבט לעתיד - פעולות צד לקוח אחד הדברים המעייפים בריאקט תמיד היה החיבור בטופס בין State לתוכן של רכיבי הקלט. אינספור ספריות נכתבו כדי לעבוד טוב יותר עם טפסים, ואינספור מתכנתים ומתכנתות התגעגעו לאיזה ng-model שיחסוך לנו את כאב הראש. ובדיוק כשכמעט איבדנו תקווה הגיעו דן אברמוב וצוות החברים עם פיצ׳ר חדש שאולי קצת יעזור לחסוך חלק מעבודת החיבור של הטפסים - וזהו Client Side Form Actions. זה עובד ככה: 1. אפשר יהיה להגדיר לאלמנט form מאפיין action שיהיה פונקציה אסינכרונית. 2. פונקציה זו תופעל כשמישהו ינסה להגיש את התוכן, ותוכל לעדכן State. 3. אם הפונקציה זורקת שגיאה השגיאה תיחשב כשגיאת render ותיתפס ב Error Boundary הקרוב. קוד? בטח הנה הדוגמה:
import { useState } from "react";

export default function Todo() {
  const [name, setName] = useState([]);
  async function copy(formData) {
    console.log(formData);
    setName(formData.get("name"));
  }
  return (
    <>
      <form action={copy}>
        <label>
          Type your name:
          <input name="name" />
        </label>
        <button type="submit">submit</button>
      </form>
      <p>Hello {name}</p>
    </>
  );
}
כשלוחצים submit מופעלת הפונקציה שמעדכנת את משתנה הסטייט. הפונקציה מקבלת את כל המידע מהטופס אוטומטית דרך הפריימוורק וכך לא צריך לחבר בין סטייט למידע שיש בקלטים. הקוד דורש את גירסה 18.3 של ריאקט שעדיין לא יצאה, אבל אפשר לראות גירסה עובדת שלו עם גירסת הפיתוח שלהם כאן: https://codesandbox.io/s/vibrant-glade-dhrfz7?file=/src/App.js. למרות שזה נראה פיצ'ר קצת מיותר, אני חושב שמהר מאוד הוא יהפוך לפינוק קטן שכולם משתמשים בו - גם ביישומים קטנים וגם בשילוב עם רידאקס או פריימוורקים גדולים יותר, כי הוא פשוט חוסך חלק מה Boilerplate שכולנו כתבנו כל הזמן.

ToCode
1 420
# טיפ קידוד נכון - ואיך זה נראה כשזה נכשל? רוב המתכנתים לא בטוחים שהקוד אי פעם יעבוד. לא בטוחים שאפשר בכלל לכתוב גירסה שעובדת למשימה איתה הם מתמודדים ולכן הם ממהרים לכתוב גירסה שעובדת, לא משנה כמה מינימליסטית. מפתחות הצפנה? בינתיים נשים אותם בקוד אחרי שיעבוד נמצא מה לעשות איתם. כתובות של שרתים? תכתוב Hard Coded בינתיים אחרי שיעבוד נבנה מסך הגדרות מסודר. טיפול בשגיאות? הצחקת אותם. בוא נראה שה flow התקין עובד לא צריך לעבוד סתם. הבעיה שאחרי שיש flow מינימלי שעובד הם כל כך שמחים שמשהו עובד שישר רצים להראות לבוס, מקבלים מבול של פידבקים ולעולם לא יהיה להם זמן להוסיף את הטיפול בשגיאות. לכן אחד הטיפים הכי קלים לקידוד נכון יותר הוא לטפל בשגיאות בזמן שכותבים את הקוד. אם כתבתי בפייתון את השורה:
with open('/etc/passwd') as f:
    ...
ממש באותו רגע אני אשאל - ואיך זה יכול להישבר? מה אם אין לי הרשאות לקובץ? מה open יעשה? ומה אם הקובץ לא קיים, מה open יעשה? ומה אם הנתיב הוא בגלל נתיב לתיקיה, מה open יעשה? והצעד הבא דורש שילוב של סבלנות וביטחון עצמי, אבל לגמרי שווה את זה - במקום לכתוב קודם כל את תוכן הלולאה, אני מתחיל מהקוד שמטפל בכל הדרכים בהן השורה שלי יכולה להישבר ומוודא שבכל דרך התוכנית תתנהג כמו שאני רוצה שתתנהג. רק אחרי שסיימתי לטפל בכישלונות אני ממשיך לטפל במצב התקין. וכן הטיפ הזה לא רלוונטי כשעושים פרוטוטייפ למערכות, אבל צריך לשים לב שאם הפרוטוטייפים שלכם נוטים "להפוך" לגירסה האמיתית, אולי שווה לבנות תהליך פיתוח טוב יותר גם עבורם.