ch
Feedback
ToCode

ToCode

前往频道在 Telegram

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

显示更多
1 419
订阅者
无数据24 小时
无数据7
无数据30
帖子存档
ToCode
1 419
# אני לא מאמין ששוב טעיתי ב useEffect יום אחד אני אפתח מוזיאון לכל הטעויות המוזרות שאני עושה עם useEffect. בינתיים אתם מוזמנים להציץ בקוד השבור שאני כתבתי ולראות כמה זמן לוקח לכם למצוא את הבעיה. הקוד משתמש ב ResizeObserver כדי להריץ קוד כל פעם שהגודל של אלמנט מסוים שהוא מסתכל עליו משתנה:
import React, { useEffect } from 'react';

// trackedRef is a reference to a DOM element
export default function ResizeHandler({ trackedRef }) {
  useEffect(() => {
    const resizeObserver = new ResizeObserver(() => {
      const height = trackedRef.current.scrollHeight;
      console.log(`Your new height is: ${height}`);
    });
    resizeObserver.observe(trackedRef.current);

    return function cancel() {
      resizeObserver.disconnect();
    }
  }, [trackedRef]);

  return <></>;
}
רואים את הבאג? מוזמנים לשתף בתגובות ולהיזהר מסכינים חדות. (נ.ב. מכל הפונקציות של ריאקט, useEffect היתה בשבילי אהבה ממבט ראשון. הגעתי אליה עם הטראומה של הכפילות בין componentDidMount ו componentDidUpdate וחשבתי שהנה יש פיתרון לכל הצרות שלי במקום אחד. בפועל חוסר תשומת לב כמו בדוגמה כאן גורם לי לייצר אינסוף באגים יצירתיים בעבודה איתה. במקרה הזה למרות שהשתמשתי ב eslint ולמרות ש eslint כולל כלל ספציפי להתמודדות עם הבאג בקוד המודבק הוא עדיין התעלם מהטעות ולא הזהיר. אין לי פה המלצות קסם חוץ מלהיות מאוד זהירים בעבודה עם useEffect, לכתוב אפקטים קצרים ולא יותר מדי מהם ולקוות שבאיטרציה הבאה של ריאקט דן אברמוב יבוא עם מנגנון יותר ידידותי).

ToCode
1 419
# הזמנה לוובינר: ניהול משתמשים עם auth0 תקן OAuth היה אחת המהפכות הגדולות של האינטרנט, הוא איפשר לבנות את מנגנוני ההזדהות שכולנו משתמשים בהם. לצערו של OAuth הוא גם מספיק מסובך בשביל שמתכנתים לא ירצו ללמוד אותו, וכך יצא שכשאנחנו כן צריכים לממש מנגנון הזדהות אנחנו מחפשים איזה Tutorial פשוט והרבה פעמים בונים לפי ההוראות דבר שלא מתאים לנו או עם טעויות. השבוע אנחנו הולכים לתקן את זה. ביום חמישי הקרוב אעביר כאן וובינר של שעה על OAuth שיכסה גם את התיאוריה וגם בניה של מערכת שרת/לקוח המשתמשת ב OAuth כדי לאפשר למשתמשים לגשת ל API בצורה מאובטחת. בוובינר נראה: 1. מה זה OAuth, מה זה OpenID Connect, ותכל׳ס איך זה עובד. 2. נכיר את Auth0, שהוא מערכת ענן שמטפלת ברוב החלקים הקשים במימוש מנגנון הזדהות ב OAuth. 3. נבנה בעזרת Auth0 שרת API שרק משתמשים רשומים יכולים לפנות אליו. 4. נבנה לקוח React שיאפשר למשתמשים "להתחבר" למערכת ולבצע פעולות בשרת ה API. בדרך נדבר על JWT, Access Tokens, ID Tokens, Refresh Tokens, על ניהול Session בעזרת Cookies או Local Storage, וכל שאלה אחרת שתהיה לכם בנושא הזדהות. הוובינר יערך בזום ביום חמישי הקרוב ה 2/6 בשעה עשר בבוקר. לפרטים והרשמה בחינם הכנסו לדף הוובינר בקישור: https://www.tocode.co.il/workshops/114

ToCode
1 419
אלמנט Layout הוא בסך הכל קומפוננטת ריאקט שמושכת את המידע ומעבירה אותו ל Homepage או About בהתאמה. רק בשביל ההבנה הוספתי כותרת משותפת לכל הדפים והדפסה של הנתיב שאנחנו כרגע מבקרים בו. זה הקוד של Layout:
function Layout() {
  const location = useLocation();
  const url = useHref(location);
  const { data, error } = useSWR(url, fetcher)

  if (error) {
    console.log(error);
    return <p>Error: {JSON.stringify(error)}</p>
  }

  return (
    <div>
      <h1>My App</h1>
      <p>Visiting: {url}</p>
      {data && <Outlet context={data} />}
    </div>
  );
}
החבר האחרון בצד של ה Layout הוא הפונקציה fetcher שבודקת אם ה url מתאים למידע שמוטמע בעמוד, כלומר אם אנחנו צריכים להעביר את המידע מהדף שהרגע נטען. אם ה url זהה אז אפשר להחזיר את המידע שבדף, ואם הוא שונה זה אומר שאנחנו במעבר עמוד ואז צריך לטעון בתור JSON את המשתנים מהנתיב החדש. זה הקוד:
const fetcher = async (url) => {
  const pageData = document.querySelector(`#page-data[data-url="${url}"]`);
  if (pageData) {
    console.log(`Reading data from page`);
    return JSON.parse(pageData.innerHTML);
  } else {
    const res = await fetch(url, { headers: { accept: 'application/json' } })
    return await res.json();
  }
}
עכשיו אני יכול להמשיך לדפים עצמם ושם למשוך את המידע ולהשתמש בו. המשיכה משתמשת בפונקציה של React Router שנקראת useOutletContext והקוד (לדוגמה של דף הבית) נראה כך:
import React, { useState } from 'react';
import { Link, useOutletContext } from "react-router-dom";

export default function Homepage() {
  const [name, setName] = useState('');
  const { page } = useOutletContext();

  return (
    <div>
      <h2>Page Name From Rails: {page}</h2>
      <label>
        Please type your name:
        <input type="text" value={name} onChange={(e) => setName(e.currentTarget.value)} />
      </label>
      {name !== '' && <p>Hello! {name}</p>}
      <Link to="/about/index">About Us</Link> 

    </div>
  );
}
## סיכום למרות שלא השתמשנו במנגנונים המובנים של ריילס, התוצאה היא קוד שיראה מוכר בצד השרת למתכנתים ריילס ובצד הלקוח למתכנתי ריאקט. בצד השרת קונטרולר פשוט צריך לאתחל משתנה ולקרוא לפונקציה respond_with:
class HomeController < ApplicationController
  def index
    @page = { page: 'Home Page' }
    respond_with(@page)
  end
end
כשאולי הדבר היחיד שמוזר כאן זה שהשם חייב להיות @page כדי להתאים לקוד שכתבנו ב layout. בצד הלקוח אני משתמש בפונקציה useOutletContext כדי למשוך את המשתנה page, שיכול להיות גם אוביקט JSON מורכב שמכיל תוצאה של שאילתה או שאילתות מסובכות, ומשתמש בו בקומפוננטה:
import React, { useState } from 'react';
import { Link, useOutletContext } from "react-router-dom";

export default function Homepage() {
  const [name, setName] = useState('');
  const { page } = useOutletContext();

  return (
    <div>
      <h2>Page Name From Rails: {page}</h2>
      <label>
        Please type your name:
        <input type="text" value={name} onChange={(e) => setName(e.currentTarget.value)} />
      </label>
      {name !== '' && <p>Hello! {name}</p>}
      <Link to="/about/index">About Us</Link> 

    </div>
  );
}
את הקוד המלא תוכלו למצוא בריפו: https://github.com/ynonp/react-rails-vite-demo

ToCode
1 419
# מדריך: יצירת אפליקציית React בתוך שרת Rails 7 - חלק 2 - העברת משתנים פוסט זה הוא חלק שני של מדריך הקמת יישום React ו Rails ב Rails 7. אם פספסתם החלק הראשון זמין בקישור: https://www.tocode.co.il/blog/2022-05-react-rails-7-vite-rails בחלק זה נראה דרך אחת להעביר משתנים בין שרת הריילס ליישום הריאקט. ## הבעיה עם משתנים בחלק הקודם של המדריך הראיתי איך לבנות שרת ריילס שמגיש ללקוח אפליקציית ריאקט. מה שלא דיברתי עליו זה הקשר בין שני החלקים - ובפרט איך אני מעביר מידע מ Rails Controller לדף בריאקט שמתאים לו. בתוכנית הדוגמה היו לנו שני Rails Controllers, אחד עבור עמוד הבית והשני עבור עמוד אודות, ושניהם לא הריצו קוד ולכן החיים היו קלים. לריילס יש מנגנון די טוב להעברת מידע מ Controller ל View שמתאים לו, ואלה הם משתני ה @ של הקונטרולר. כלומר אני יכול לכתוב בקוד של קונטרולר:
def index
    @page_name = "Homepage"
end
ואחרי זה ב View אני יכול פשוט להשתמש ב @page_name בתוך ה HTML של העמוד:
<h1><%= @page_name %></h1>
הבעיה שאנחנו כבר לא בקנזס - או יותר נכון לא משתמשים ב Views של Rails. מה שיותר גרוע הוא שכל מנגנון מעברי הדפים שלנו משתמש ב React Router ולא ב Turbolinks, ולכן אפילו אם היינו מצליחים לגרום לדף הראשון להציג מידע דרך ה Views (למשל עם ג'ם כמו Gon), אחרי מעבר עמוד לא היה שום דבר שגורם לעמוד החדש להציג את המידע המעודכן. תשמחו לשמוע שבגלל הגמישות של ריילס וריאקט מאוד קל לבנות בעצמנו מנגנון שיראה מאוד דומה למנגנון ברירת המחדל אבל יעבוד בריאקט. ## פיתרון באמצעות REST API הפיתרון שנבנה יתבסס על ג'ם שנקרא responders. ג'ם זה מאפשר להגדיר שכל קונטרולר יוכל להגיב לכל מיני סוגים של בקשות - במקרה שלנו נגדיר שאם מבקשים מקונטרולר דף HTML הוא ישלח דף רגיל כמו שעשה עד עכשיו, וישתול את המשתנים בתוך אלמנט נסתר בדף הזה. אבל, אם מבקשים מאותו קונטרולר לקבל JSON, אז הוא ישלח רק את המשתנים בתור JSON. תחילה אני מתקין את הג'ם עם:
$ bundle add responders
עכשיו אני הולך ל application_controller.rb ומגדיר לו שהוא וכל קונטרולר שיורש ממנו יסכים לענות לבקשות ל html וגם לבקשות ל json:
class ApplicationController < ActionController::Base
  respond_to :html, :json
end
נמשיך ל home_controller.rb ונגדיר שם משתנה, אני אקרא לו @page, שיכיל אוביקט עם מפתח בשם page שהתוכן שלו הוא כותרת העמוד:
class HomeController < ApplicationController
  def index
    @page = { page: 'Home Page' }
    respond_with(@page)
  end
end
אותו דבר אני כותב ב about_controller.rb רק עם כותרת עמוד שונה:
class AboutController < ApplicationController
  def index
    @page = { page: 'About Page' }
    respond_with(@page)
  end
end
הפקודה respond_with מגיעה מהג'ם responder והיא המקור לרוב הקסם כאן. היא מתאימה לפקודת ה respond_to שכתבנו בראש הקונטרולר הראשי. באופן אוטומטי respond_with יודעת לקחת את המשתנה שנתנו לה ולהפוך אותו ל JSON אם מישהו מבקש JSON. בשביל לשתול את המשתנה הזה ב HTML אנחנו צריכים לכתוב קצת קוד. נלך ל app/views/layout/application.html.erb ושם מתחת ל body אני מוסיף את הבלוק הבא:
<% if defined?(@page) %>
    <script type="text/json" id="page-data" data-url="<%= request.fullpath %>">
    <%= raw @page.to_json %>
    </script>
<% end %>
עכשיו בכל טעינת עמוד ריילס ישתול בתוכו אלמנט script עם תוכן המשתנה @page, ובכל מעבר עמוד ריילס ישלח את תוכן המשתנה @page בתור אוביקט JSON ללקוח. ## תיקון צד לקוח בצד של ריאקט אני צריך למשוך את המידע ולהעביר אותו לדפים. דרך קלה לעשות את זה היא לרנדר אלמנט Layout כללי לכל הדפים שיהיה אחראי על משיכת המידע מהשרת או מהתוכן שכבר מוטמע ב HTML, ויעביר אותו פנימה לאלמנט שמתאים לדף הנוכחי. בשביל להוסיף אלמנט Layout מסביב לאלמנט דף הבית או דף האודות אני משתמש בקוד הבא בקובץ entrypoints/application.jsx:

function App() {
  return (
    <Router>
        <Routes>
          <Route element={<Layout />}>
            <Route path="/" element={<Homepage />} />
            <Route path="/about/index" element={<About />} />
          </Route>
        </Routes>
    </Router>
  );
}

ToCode
1 419
גם grep עובד על פתקים ולכן אני יכול לסנן רק קומיטים שמכילים את הטקסט All passed בפתק הבדיקות:
$ git log --notes=testing --oneline --grep "All passed"

4126289 added about page
Notes (testing):
    performed 10 tests. All passed
## שליחת פתקים לשרת מרוחק בעבודה עם מאגר מרוחק אולי לא תשמחו לשמוע שהפתקים אינם נשלחים כברירת מחדל למאגר בכל push שאתם מפעילים. הפקודה הבאה דוחפת את המאגר יחד עם הפתקים:
git push origin "refs/notes/*"
וגם במשיכה ממאגר מרוחק לא תקבלו את הפתקים, ובשביל למשוך אותם תצטרכו להפעיל:
git fetch origin "refs/notes/*:refs/notes/*"
אם מוגדר לפרויקט שלכם שרת מרוחק בשם origin, הפקודות הבאות יגרמו לכל פעולת push ו fetch לכלול גם את הפתקים
$ git config --add remote.origin.push '+refs/notes/*:refs/notes/*'

$ git config --add remote.origin.fetch '+refs/notes/*:refs/notes/*'

ToCode
1 419
# מהם Git Notes ואיך הם עוזרים לנו לעשות סדר במאגר גיט הפיצ'ר של git notes קיים בגיט כבר עשר שנים בערך אבל מעט אנשים מכירים אותו או משתמשים בו. בפוסט זה נראה מהם notes ומה הפקודות המרכזיות לעבודה איתם. ## מי צריך פתקים אתגר משמעותי בעבודה עם גיט הוא שקומיטים אינם ניתנים לעריכה. נכון, אפשר למחוק קומיט וליצור אחר במקומו (עם פקודות כמו ריבייס, אמנד, צ'רי-פיק), אבל ממש לקחת קומיט קיים שדחפתי לשרת ושאחרים התבססו עליו ובנו ממנו קומיטים שלהם, ולשנות משהו בהודעה שלו, זה כבר לא פשוט. וכך נוצרו מנגנונים שעוזרים לנו להוסיף גמישות לגרף הקומיטים - מנגנונים כמו בראנצ'ים, תגיות וגם פתקים. פתק הוא טקסט חופשי שאני יכול להדביק על קומיט. הפתק יופיע בלוג (אם ארצה להציג אותו שם), הוא ניתן למחיקה, עריכה והזזה ואני יכול לחפש קומיטים לפי טקסט שיש בפתקים שלהם. ולמה זה טוב? הנה כמה דוגמאות: 1. אם יש לי סקריפט שמריץ Deployment אוטומטי לסביבת בדיקות אחרי שעשיתי קומיט, אני יכול להדביק את הפלט של הסקריפט הזה בתור פתק לקומיט אחרי שהוא עושה דיפלוי. בצורה כזאת אני יכול לוודא שה deploy הצליח ולהיזכר אחר כך איזה קומיטים נכנסו ומתי לסביבת הבדיקות. 2. אותו סקריפט יכול לעשות deploy לסביבת פרודקשן, ואז הוא שוב יוסיף לי הערה לקומיט שהקוד נכנס לסביבת פרודקשן. 3. הרבה פעמים אני משתמש בלוג של גיט כדי לייצר רשימת שינויים כשאני מוציא גירסה. את הטקסט של רשימת השינויים אני רוצה לערוך גם אחרי שיצרתי את הודעת הקומיט, ולכן אני יכול להוסיף פתק על כל קומיט מה השינויים שהוא הביא, לערוך את הפתקים האלה כשצריך ובסוף להוציא רשימת שינויים מסודרת. 4. מאגרים מסוימים כוללים Pipelines שמריצים בדיקות אוטומטיות על כל קומיט, ופרויקטים אחרים מעסיקים אנשי QA שיריצו בדיקות בצורה ידנית. בשני המקרים התוצאה של הבדיקה לא יכולה להופיע בתור הודעת הקומיט (כי היא לא היתה זמינה כשעשינו קומיט), אבל אנחנו יכולים להדביק אותה בתור פתק לקומיט המתאים. אחר כך נוכל לחפש בין תוצאות הבדיקות או לסנן רק קומיטים בהם כל הבדיקות עברו. ## איך משתמשים בפתקים פתק הוא מידע שנשמר במאגר של גיט בקובץ נפרד מהקומיט. אנחנו יכולים להוסיף כמה פתקים שרוצים על כל קומיט בתנאי שהם יהיו ב"נושאים" שונים. אני יוצר מאגר ראשון בשביל הדוגמה עם הפקודות:
$ git init .
$ git commit --allow-empty -m 'initial commit'
$ git commit --allow-empty -m 'created homepage'
$ git commit --allow-empty -m 'added about page'
$ git commit --allow-empty -m 'added admin panel'
$ git commit --allow-empty -m 'allow editing header from the admin pages'
התוצאה היא:
$ git log --oneline
0fa947b allow editing header from the admin pages
f4fa771 added admin panel
4126289 added about page
b4a7379 created homepage
ebddbeb initial commit
עכשיו בואו נוסיף כמה פתקים. נדמיין שיש לנו סקריפט שיודע לשלוח את הפרויקט לסביבת פרודקשן אחרי כל קומיט, ואותו סקריפט רוצה לדווח שהוא עשה את העבודה. הפקודה הבאה מוסיפה פתק מסוג deployment לקומיט הראשון במאגר:
$ git notes --ref=deploy append ebddbeb -m 'pushed to production 22/10/22 10:00'
והפקודה הבאה תוסיף פתק דומה לקומיט השני:
$ git notes --ref=deploy append b4a7379 -m 'pushed to production 25/10/22 10:00'
עכשיו נניח שיש לנו סקריפט נוסף שמריץ בדיקות אוטומטיות. בשני הקומיטים הראשונים לא היו עדיין בדיקות אבל בקומיט השלישי כבר כתבתי 10 בדיקות, הסקריפט הריץ אותן וכולן עברו. אני יכול לדמיין את הפקודות הבאות שמוסיפות פתקים רלוונטים מאותו סקריפט בדיקות:
$ git notes --ref=testing append ebddbeb -m 'no tests found'
$ git notes --ref=testing append b4a7379 -m 'no tests found'
$ git notes --ref=testing append 4126289 -m 'performed 10 tests. All passed'
עכשיו שכל הפתקים במקום אני יכול להציג את הלוג ולהוסיף לו פתקים מסוג מסוים. לדוגמה זה הלוג בתוספת כל הפתקים מסוג testing:
0fa947b allow editing header from the admin pages
f4fa771 added admin panel
4126289 added about page
Notes (testing):
    performed 10 tests. All passed

b4a7379 created homepage
Notes (testing):
    no tests found

ebddbeb initial commit
Notes (testing):
    no tests found
וכדי להציג את כל הפתקים מכל הסוגים אני יכול להשתמש בכוכבית:
$ git log --notes="*"

ToCode
1 419
# מהם Git Notes ואיך הם עוזרים לנו לעשות סדר במאגר גיט הפיצ'ר של git notes קיים בגיט כבר עשר שנים בערך אבל מעט אנשים מכירים אותו או משתמשים בו. בפוסט זה נראה מהם notes ומה הפקודות המרכזיות לעבודה איתם. ## מי צריך פתקים אתגר משמעותי בעבודה עם גיט הוא שקומיטים אינם ניתנים לעריכה. נכון, אפשר למחוק קומיט וליצור אחר במקומו (עם פקודות כמו ריבייס, אמנד, צ'רי-פיק), אבל ממש לקחת קומיט קיים שדחפתי לשרת ושאחרים התבססו עליו ובנו ממנו קומיטים שלהם, ולשנות משהו בהודעה שלו, זה כבר לא פשוט. וכך נוצרו מנגנונים שעוזרים לנו להוסיף גמישות לגרף הקומיטים - מנגנונים כמו בראנצ'ים, תגיות וגם פתקים. פתק הוא טקסט חופשי שאני יכול להדביק על קומיט. הפתק יופיע בלוג (אם ארצה להציג אותו שם), הוא ניתן למחיקה, עריכה והזזה ואני יכול לחפש קומיטים לפי טקסט שיש בפתקים שלהם. ולמה זה טוב? הנה כמה דוגמאות: 1. אם יש לי סקריפט שמריץ Deployment אוטומטי לסביבת בדיקות אחרי שעשיתי קומיט, אני יכול להדביק את הפלט של הסקריפט הזה בתור פתק לקומיט אחרי שהוא עושה דיפלוי. בצורה כזאת אני יכול לוודא שה deploy הצליח ולהיזכר אחר כך איזה קומיטים נכנסו ומתי לסביבת הבדיקות. 2. אותו סקריפט יכול לעשות deploy לסביבת פרודקשן, ואז הוא שוב יוסיף לי הערה לקומיט שהקוד נכנס לסביבת פרודקשן. 3. הרבה פעמים אני משתמש בלוג של גיט כדי לייצר רשימת שינויים כשאני מוציא גירסה. את הטקסט של רשימת השינויים אני רוצה לערוך גם אחרי שיצרתי את הודעת הקומיט, ולכן אני יכול להוסיף פתק על כל קומיט מה השינויים שהוא הביא, לערוך את הפתקים האלה כשצריך ובסוף להוציא רשימת שינויים מסודרת. 4. מאגרים מסוימים כוללים Pipelines שמריצים בדיקות אוטומטיות על כל קומיט, ופרויקטים אחרים מעסיקים אנשי QA שיריצו בדיקות בצורה ידנית. בשני המקרים התוצאה של הבדיקה לא יכולה להופיע בתור הודעת הקומיט (כי היא לא היתה זמינה כשעשינו קומיט), אבל אנחנו יכולים להדביק אותה בתור פתק לקומיט המתאים. אחר כך נוכל לחפש בין תוצאות הבדיקות או לסנן רק קומיטים בהם כל הבדיקות עברו. ## איך משתמשים בפתקים פתק הוא מידע שנשמר במאגר של גיט בקובץ נפרד מהקומיט. אנחנו יכולים להוסיף כמה פתקים שרוצים על כל קומיט בתנאי שהם יהיו ב"נושאים" שונים. אני יוצר מאגר ראשון בשביל הדוגמה עם הפקודות:
$ git init .
$ git commit --allow-empty -m 'initial commit'
$ git commit --allow-empty -m 'created homepage'
$ git commit --allow-empty -m 'added about page'
$ git commit --allow-empty -m 'added admin panel'
$ git commit --allow-empty -m 'allow editing header from the admin pages'
התוצאה היא:
$ git log --oneline
0fa947b allow editing header from the admin pages
f4fa771 added admin panel
4126289 added about page
b4a7379 created homepage
ebddbeb initial commit
עכשיו בואו נוסיף כמה פתקים. נדמיין שיש לנו סקריפט שיודע לשלוח את הפרויקט לסביבת פרודקשן אחרי כל קומיט, ואותו סקריפט רוצה לדווח שהוא עשה את העבודה. הפקודה הבאה מוסיפה פתק מסוג deployment לקומיט הראשון במאגר:
$ git notes --ref=deploy append ebddbeb -m 'pushed to production 22/10/22 10:00'
והפקודה הבאה תוסיף פתק דומה לקומיט השני:
$ git notes --ref=deploy append b4a7379 -m 'pushed to production 25/10/22 10:00'
עכשיו נניח שיש לנו סקריפט נוסף שמריץ בדיקות אוטומטיות. בשני הקומיטים הראשונים לא היו עדיין בדיקות אבל בקומיט השלישי כבר כתבתי 10 בדיקות, הסקריפט הריץ אותן וכולן עברו. אני יכול לדמיין את הפקודות הבאות שמוסיפות פתקים רלוונטים מאותו סקריפט בדיקות:
$ git notes --ref=testing append ebddbeb -m 'no tests found'
$ git notes --ref=testing append b4a7379 -m 'no tests found'
$ git notes --ref=testing append 4126289 -m 'performed 10 tests. All passed'
עכשיו שכל הפתקים במקום אני יכול להציג את הלוג ולהוסיף לו פתקים מסוג מסוים. לדוגמה זה הלוג בתוספת כל הפתקים מסוג testing:
0fa947b allow editing header from the admin pages
f4fa771 added admin panel
4126289 added about page
Notes (testing):
    performed 10 tests. All passed

b4a7379 created homepage
Notes (testing):
    no tests found

ebddbeb initial commit
Notes (testing):
    no tests found
וכדי להציג את כל הפתקים מכל הסוגים אני יכול להשתמש בכוכבית:
$ git log --notes="*"

ToCode
1 419
# היום למדתי: מה באמת הפריע כל כך ב class ב React בין הצעקות הראשונות שאנחנו פוגשים בקונסול כשכותבים ריאקט נמצאת הצעקה:
Warning: Invalid DOM property `class`. Did you mean `className`?
אבל כבר כמה שנים למרות האזהרה הקוד עדיין עובד. כלומר בקומפוננטה הזו:
import "./styles.css";

export default function App() {
  return (
    <div class="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}
ל div המרכזי יהיה את הקלאס App. אז מה בכל זאת הבעיה? מסתבר שהסיפור הוא בכלל לא מגבלה טכנית של ריאקט אלא ניסיון להגן עלינו המתכנתים. בשביל לכתוב קומפוננטה שמקבלת בתור prop מאפיין class יכול להיות שהיינו מנסים לכתוב קוד כזה:
export default function App(props) {
    const { class } = props;
}
שלא היה עובד בגלל ש class זו מילה שמורה. וכן ברור שהיינו יכולים לכתוב במקום משהו כזה:
export default function App(props) {
    const { class: className } = props;
    // now I use className inside the component, to handle the "class" from outside
}
אבל אף אחד לא רוצה לשנות עכשיו את כל הקוד של קומפוננטות קיימות. ולמה לא לתמוך בשני הכתיבים (מה שכבר קורה עכשיו) בלי אזהרה? התשובה שאם לא היתה אזהרה אז כל ספריית קומפוננטות היתה צריכה לתמוך בשני הכתיבים וזה שוב יכריח כותבי ספריות לשכתב הרבה קוד.

ToCode
1 419
/dailypost

ToCode
1 419
# מתי להמשיך הלאה? אנחנו יודעים שיש מינימום זמן עבודה עם טכנולוגיה כדי שנהיה יעילים בה - מה שנקרא Ramp Up Time. אם בילית סופשבוע עם גו כנראה שזה לא מספיק, ועבור רוב האנשים פחות משנה של ניסיון ב Rails לא יספיק בשביל להשתתף בפרויקט גדול. אבל האם יש גם מקסימום זמן עבודה עם טכנולוגיה, שאחריו המשך עבודה איתה הוא בזבוז זמן? האם אפשר להיות מספיק מנוסה ב Rails? האם יש נקודה שבה אין שום פרויקט שילמד אותך משהו חדש על הטכנולוגיה? לדעתי התשובה שלילית. עד עכשיו ובכל טכנולוגיה שעבדתי איתה, כל פעם שלמדתי משהו חדש וכל ניסיון נוסף שהיה לי עם הטכנולוגיה רק פתח לי דלתות לנושאים חדשים נוספים שצריך ללמוד. אם נישאר עם הדוגמה של ריילס כל אלה פרויקטים שאפשר לעשות וללמוד מהם דברים חדשים- 1. לכתוב אתר בסיסי עם מערכת ניהול 2. לכתוב רשת חברתית 3. לכתוב אתר כמו ויקס - שכל אחד יכול ליצור תוכן לעצמו 4. לכתוב מערכת של חנות דיגיטלית 5. לכתוב API ולתקשר איתו מתוך אפליקציית Front End 6. לכתוב Rails Engine המשותף למספר פרויקטים 7. לכתוב ולהפיץ Rails Gem 8. לשכתב את ה Rails Gem שלך כשיש לך הרבה משתמשים ואתה מגלה שה API כבר לא מספיק טוב 9. לכתוב מערכת Real Time Web בה גולשים משתפים תוכן ורואים תוכן של אחרים בזמן אמת 10. לתקן בעיות אבטחה בשרת ריילס שנפרץ 11. להקים מערכת בדיקות לאפליקציית ריילס גדולה, עם חלוקה טובה לסוגי הבדיקות 12. לתקן בעיות ביצועים ו Deadlocks במערכת גדולה עקב עומס משתמשים והרשימה כמובן יכולה להתארך עוד ועוד, וככל שנהיה יותר ספציפיים היא תהיה יותר ארוכה. בעולם הטכנולוגי של היום מיומנות שווה הרבה, וככל שהמיומנות גבוהה יותר היא שווה יותר. אתם לא בקצה, אתם אפילו לא קרובים לשם. המשך לימוד, המשך פוקוס וחיפוש פרויקטים חדשים ברמה יותר גבוהה אבל באותה טכנולוגיה הוא בדרך כלל הדרך הטובה ביותר להתקדם כמתכנתים. (נ.ב. כן יש טכנולוגיות מתות. זה לא עוזר להיות מומחה סימביאן היום. וכן גם הטכנולוגיות שאנחנו עכשיו עובדים עליהן ימותו. אבל אם עבדת 6 שנים בריאקט ואת מתלבטת אם להמשיך בריאקט או לעבור לאנגולר, משתלם לחפש פרויקט ריאקט טוב יותר).

ToCode
1 419
/dailypost

ToCode
1 419
# שמונה עשרה תרגילי גיט כדי להבין איך עובדים עם Remotes לא מזמן פרסמתי פה סידרה של שמונה עשרה תרגילי גיט שמתרגלים עבודה עם קומיטים וענפים על מכונה מקומית וכבר חודש שאני מחפש הזדמנות לכתוב את החלק השני של הרשימה - תרגילי גיט שיעזרו לנו להתאמן על עבודה עם Git Remote Repositories. ## 18 תרגילי גיט על עבודה עם מאגרים מרוחקים 1. צרו מאגר ריק (Bare Repository) בתיקיה חדשה על המחשב שלכם 2. צרו Clone של המאגר בתיקיה בשם clone1 3. צרו קובץ חדש ב clone1, בצעו קומיט, ואז שנו אותו כמה פעמים והמשיכו לעשות קומיטים. בסוף הפעילו push כדי לדחוף את כל הקומיטים למאגר. בצעו את אותו דבר ב branch חדש ודחפו גם אותו. 4. וודאו שאתם מצליחים לראות את כל הקומיטים והענף החדש במאגר הריק המקורי. 5. צרו Clone חדש של המאגר בתיקיה בשם clone2. עכשיו יש לנו שני משתמשים שעובדים על מאגר משותף. וכל זה בלי גיטהאב. 6. צרו קומיט ב clone1, דחפו אותו למאגר המשותף והפעילו pull מ clone2. 7. צרו קומיט ב clone1, דחפו אותו ואז ב clone2 הפעילו fetch ואז merge ידנית. 8. צרו קומיט חדש ב clone1 וקומיט אחר ב clone2. הפעילו push מ clone1 ו fetch מ clone2, ואז השתמשו ב merge כדי למזג את השינויים ל clone2. דחפו למאגר את התוצאה ומשכו אותה מ clone1. 9. צרו קומיט חדש ב clone1 וקומיט אחר ב clone2. הפעילו push מ clone1 ו fetch מ clone2, ואז השתמשו ב rebase כדי למזג את השינויים ב clone2. אחרי שהכל במקום הפעילו push ו pull כדי לסנכרן את שני העותקים. 10. חזרו על סעיפים 8 ו-9, הפעם השתמשו ב git pull וב git pull --rebase בהתאמה במקום ב fetch. 11. צרו ענף חדש ב clone1, צרו קומיט בענף זה ודחפו למאגר. הפעילו fetch מ clone2. האם אתם יכולים לראות את הענף החדש? 12. ב clone1 תקנו את הודעת הקומיט של הקומיט האחרון עם git commit --amend. הפעילו pull ב clone2. האם אתם יכולים לראות את השינוי? האם משהו נשבר? 13. עכשיו הגיע הזמן לשבור דברים: - עברו לענף main בשני העותקים - ב clone1 צרו קומיט ודחפו - ב clone2 הפעילו pull, צרו קומיט חדש ודחפו - עכשיו ב clone1 תקנו את הודעת הקומיט האחרונה עם git commit --amend. - ב clone2 הפעילו pull - מה קרה? 14. תקנו את הקונפליקט שנוצר, בצעו קומיט ודחפו. אל תפעילו עדיין pull ב clone1. בשני המאגרים הפעילו git log --oneline --graph. מה הבעיה שאתם מזהים בהיסטוריה? 15. הפעילו git pull ב clone1. הפעילו git log --oneline --graph שוב. וודאו ששני המאגרים מסונכרנים ושאותה בעיה בהיסטוריה מופיעה עכשיו בשניהם. 16. חזרו על תרגילים 13, 14 ו 15 אך הפעם השתמשו ב git pull --rebase במקום ב pull. מה קרה הפעם? 17. השתמשו ב git reflog בתיקיית clone2 כדי למצוא את הקומיט שנעלם לכם בריבייס.A 18. השתמשו ב git cherry-pick כדי לשחזר את הקומיט הנעלם. ## הערה על פיתרונות בפעם הקודמת שפרסמתי רשימה כזאת הרבה אנשים שאלו על פיתרונות. אני לא חושב שפירסום פיתרונות יוסיף לפוסט כזה או יועיל למי שקורא אותו. הדרך להתיחס לפוסט כזה היא לקחת מפה רעיונות לדברים ששווה לנסות לבד. אם אתם לא יודעים איך להשתמש באחת הפקודות כדי לנסות לבד את התרגיל אז שווה לקרוא עליה. אפשר ורצוי לחפש בגוגל. המטרה היא לא "לדעת" את כל התשובות, אלא לבלות יותר זמן עם רשימת התרגילים הזאת, לבלות יותר זמן במשחק עם גיט ויותר זמן בחיפוש וקריאה על גיט ברשת.

ToCode
1 419
אבל זה לא הכל - בעבודה עם ריאקט יש פלאגין שנקרא react refresh שחייבים לטעון מתוך ה HTML. ויט היה מעדכן את ה HTML אם היה יכול, אבל אנחנו מגישים את ה HTML דרך ריילס ולכן צריך להוסיף את העדכון ידנית לקובץ ה layout. אחרי העדכון הקובץ app/views/layouts/application.html.erb נראה כך:
<!DOCTYPE html>
<html>
  <head>
    <title>HelloReactWorld1</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>


    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>

<script type="module">
import RefreshRuntime from "/vite-dev/@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
</script>

    <%= vite_client_tag %>
  </head>

  <body>
    <div id="app"></div>
    <%= vite_javascript_tag 'application.jsx' %>
  </body>
</html>
## פיתוח קוד צד לקוח נמשיך לכתיבת קוד ריאקט שיורכב מדף בית, דף אודות וקובץ ניתוב. בשביל דף הבית אני יוצר תיקיה חדשה:
$ mkdir app/javascript/routes
ובתוכה יוצר את הקובץ app/javascript/routes/homepage.jsx עם התוכן הבא:
import React, { useState } from 'react';
import { Link } from "react-router-dom";

export default function Homepage() {
  const [name, setName] = useState('');

  return (
    <div>
      <label>
        Please type your name:
        <input type="text" value={name} onChange={(e) => setName(e.currentTarget.value)} />
      </label>
      {name !== '' && <p>Hello! {name}</p>}
      <Link to="/about/index">About Us</Link> 

    </div>
  );
}
ובקובץ app/javaascript/entrypoints/application.jsx אני מוחק את כל התוכן ובמקומו כותב את התוכן הבא:
import React from "react";
import { createRoot } from 'react-dom/client';
import Homepage from "../routes/homepage";
import About from '../routes/about';
import {
  BrowserRouter as Router,
  Route,
  Routes,
} from "react-router-dom";

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Homepage />} />
        <Route path="/about/index" element={<About />} />
      </Routes>
    </Router>
  );
}

const root = createRoot(document.querySelector('#app'));
root.render(<App />);
הקובץ השלישי של היישום נקרא app/javascript/routes/about.jsx ובו אני כותב את התוכן הבא:
import React from 'react';

import { Link } from "react-router-dom";

export default function About() {
  return (
    <div>
      <p>About Us</p>
      <Link to="/">Back Home</Link> 
    </div>
  );
}
## הרצה ובדיקה אנחנו מוכנים לצאת לדרך. נפעיל בחלון נפרד את הפקודה:
$ ./bin/vite dev
ובחלון נוסף את הפקודה:
$ ./bin/rails s
ועכשיו אפשר להיכנס מדפדפן ל localhost:3000 ולראות את דף הריאקט הראשון שיצרנו, ללחוץ על הקישור ולהגיע לדף השני.

ToCode
1 419
# מדריך: יצירת אפליקציית React בתוך שרת Rails 7 זה מרגיש כאילו רק אתמול פרסמתי כאן מדריך איך לעבוד עם React ב Rails 6 והנה יצאה ריילס 7 עם גישה חדשה לגמרי לעבודה עם JavaScript. בפוסט זה נראה את הג'ם vite-ruby שמאפשר לנו לבנות אפליקציית ריאקט עם ריילס 7 גם בלי webpacker. השינוי הגדול המרכזי של Rails 7 ביחס לעבודה עם JavaScript הוא ההחלטה לוותר על שלב ה Precompilation. הטענה של DHH היא שהטכנולוגיות בשלות: עם HTTP Push אפשר להחליף את ה Bundling בלי לאבד ביצועים, ודפדפנים יודעים כבר להריץ JavaScript ברמה טובה כך שלא צריך Babel. נכון, אנחנו מפסידים את scss אבל אולי זה לא סוף העולם. הקורבן הגדול ביותר של ההחלטה הזאת הוא דווקא JSX, כי בלי pre-compilation אין מי שיהפוך את ה JSX-ים לקוד JavaScript רגיל. לפני כמה ימים כתבתי על ספריית htm והיא יום אחד תהיה המפתח לחיבור. הבעיה שכרגע העבודה עם jspm ובאופן כללי עם import maps לא מספיק יציבה - הרבה מודולים ב jspm ייכשלו אם ננסה להוריד אותם אלינו, ואחרים ייכשלו בכל מקרה. כן אפשר לתקן כל תקלה, אבל אם אתם רוצים שהכלים יעבדו בשבילכם ולא אתם בשביל הכלים אני ממליץ בינתיים לחכות עם import maps. מצד שני הג'ם webpacker נמצא ממש בסוף דרכו המקצועית ולכן נצטרך דרך אחרת לשלב בין קוד ריילס לקוד פרונט אנד. הדרך הזאת היא הג'ם vite-rails. התוכנית שלנו לפוסט: 1. נבנה אפליקציית ריילס ואפליקציית ריאקט שמחוברת אליה באמצעות הג'ם vite-rails. 2. נעביר משתנים מריילס לריאקט באמצעות הג'ם gon. 3. נשתמש בריאקט ראוטר כדי לטעון את הדף המתאים באפליקציית הריאקט. מוכנים? בואו נצא לדרך. ## הקמת אפליקציית ריילס חדשה קודם כל אני מוודא שריילס 7 מותקן לי על המחשב עם:
$ rails --version
Rails 7.0.3
יוצר אפליקציה חדשה:
$ rails new HelloReactWorld1
נכנס לתיקיה:
$ cd HelloReactWorld1
בגלל שאני צריך שכל ה Controllers באפליקציה יגיעו לריאקט, אני מעדיף לעדכן את ה Layout שיתאים לי לריאקט ולא להשתמש בשכבת ה Views של ריילס. אני מעדכן את הקובץ app/views/layouts/application.html.erb וכותב בו את התוכן הבא:
<!DOCTYPE html>
<html>
  <head>
    <title>HelloReactWorld1</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>

  <body>
    <div id="app"></div>
  </body>
</html>
עכשיו הגיע הזמן ליצור שני קונטרולרים בשביל שני דפים באתר. משורת הפקודה אני מריץ:
$ rails g controller home index
$ rails g controller about index
ונעדכן את הקובץ config/routes.rb לתוכן הבא:
Rails.application.routes.draw do
  root to: 'home#index'
  get 'about/index'
end
## הוספת אפליקציית צד לקוח עם vite משורת הפקודה מתקינים את הג'ם:
$ bundle add vite_rails
מייצרים את הסטארטרים עם:
$ bundle exec vite install
ועכשיו צריך להתקין את התמיכה בריאקט. מעדכנים את הקובץ vite.config.ts שנוצר בתיקיה הראשית של הפרויקט לתוכן הבא:
import { defineConfig } from 'vite';
import RubyPlugin from 'vite-plugin-ruby';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    RubyPlugin(),
    react(),
  ],
})
מפעילים משורת הפקודה בתיקיה הראשית של הפרויקט את הפקודה:
$ npm install --save @vitejs/plugin-react react react-dom react-router-dom@6 react-refresh
ואנחנו כמעט מוכנים. נקודת הכניסה שלנו למערכת היא הקובץ app/javascript/entrypoints/application.js, אבל אני הייתי מעדיף שהוא יהיה עם הסיומת jsx כדי שאוכל לכתוב בו קוד jsx. לכן נשנה לו את השם ל application.jsx:
$ mv app/javascript/entrypoints/application.js app/javascript/entrypoints/application.jsx
ובמקביל בקובץ app/views/layouts/application.html.erb אני מחליף את השורה שמתחילה ב vite_javascript_tag ובמקומה כותב:
<%= vite_javascript_tag 'application.jsx' %>