React היא ספריית JavaScript לעיבוד ממשקי משתמש (UI). ממשק המשתמש בנוי מיחידות קטנות כמו לחצנים, טקסט ותמונות. React מאפשרת לך לשלב אותם לרכיבים הניתנים לשימוש חוזר, הניתנים לקינון.
לדוגמה:
function Profile() {
return (
<img
src="https://i.imgur.com/MK3eW3As.jpg"
alt="Katherine Johnson"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}
השאלה העיקרית שעולה כאן היא כיצד הכנסנו קוד שנראה כמו HTML לתוך קומפוננטה שכתובה ב js. נדבר על זה בהמשך כי זה הליבה שעליה מבוסס React.
ניתן להצהיר על רכיבים רבים בקובץ אחד, אבל קבצים גדולים יכולים להיות קשים לניווט. כדי לפתור זאת, אפשר לייצא רכיב לקובץ משלו, ולאחר מכן לייבא את הרכיב הזה מקובץ אחר:
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}
זאת דרך ייצוא נפוצה ב js , בשיטה זאת מתייחסים לקומפוננטות כפונקציות. ניתן להתייחס אליהן גם ב class אבל לא אתמקד בזה כאן.
JSX היא החרבת סינתקס עבור JavaScript המאפשרת לך לכתוב סימון דמוי HTML בתוך קובץ JavaScript. למרות שיש דרכים אחרות לכתוב רכיבים, רוב מפתחי React מעדיפים את התמציתיות של JSX, ורוב בסיסי הקוד משתמשים בזה.
האינטרנט נבנה על HTML, CSS ו-JavaScript. במשך שנים רבות, מפתחי אתרים שמרו על תוכן ב-HTML, עיצוב ב-CSS והיגיון ב-JavaScript - לעתים קרובות בקבצים נפרדים! התוכן סומן בתוך HTML בעוד ההיגיון של הדף חי בנפרד ב-JavaScript:

אבל ככל שהרשת הפכה לאינטראקטיבית יותר, הלוגיקה קבעה יותר ויותר את התוכן.
JavaScript התחיל להיות אחראי על ה HTML וזו הסיבה שב-React, רינדור הלוגיקה והdesign חיים יחד באותו מקום - כקומפוננטה.

כל רכיב React הוא פונקציית JavaScript שעשויה להכיל markup כלשהו ש-React מרנדר לדפדפן. רכיבי React משתמשים בהרחבת סינתקס הנקראת JSX כדי לייצג את הסימון הזה.
JSX ו React הם שני דברים נפרדים שלרוב משתמשים בהם ביחד. עם זאת, ניתן להשתמש בכל אחד מהם בנפרד.
נניח שיש לנו את הHTML הבא
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo" >
<ul>
<li>Invent new traffic lights
<li>Rehearse a movie scene
<li>Improve the spectrum technology
</ul>
נרצה לשים את הHTML הזה בקומפוננטה שלנו שנקרא TodoList
export default function TodoList() {
return (
// ???
)
}
אם פשוט נעתיק לתוך הקומפוננטה את ה html נקבל שגיאה


זה נובע בגלל ש JSX היא טיפה יותר מחמירה ויש לה קצת יותר חוקים מ HTML.

או

התגית הריקה נקראת Fragment
JSX נראה כמו HTML, אבל מתחת למכסה המנוע הוא הופך לאובייקטי JavaScript פשוטים. לא ניתן להחזיר שני אובייקטים מפונקציה מבלי לעטוף אותם במערך. זה מסביר מדוע אתה גם לא יכול להחזיר שני תגי JSX מבלי לעטוף אותם בתג אחר או פרגמנט.
<img> ב JSX חייבים לסגור אותם ידנית על ידי </ .stroke-width יש להחליף ב strokeWitdh. מקרה קצת חריג הוא class שכן זאת מילה שמורה בשפה javascript ולכן הוחלפה ב className.באמצעות סוגריים מסולסלים {} ניתן להעביר פרמטרים של javascript למשל
export default function Avatar() {
const avatar = 'https://i.imgur.com/7vQD0fPs.jpg';
const description = 'Gregorio Y. Zara';
return (
<img
className="avatar"
src={avatar}
alt={description}
/>
);
}
בגלל ש JSX היא בסך הכל הרחבת סינתקס של JS , נוכל להשתמש בה כדי לכתוב JS בתוכה, יתרון עצום על פני HTML למשל:
<h1>{name}'s To Do List</h1>
ואפילו נוכל לקרוא לפונקציות
<h1>To Do List for {formatDate(today)}</h1>
בגלל שהסינתקס של סוגריים מסולסלות מייצג גם אובייקטים אם נרצה להעביר ב JSX אובייקט נצטרך לפתוח שתי סוגריים למשל

על הדרך גם נראה שניתן להעביר style כאובייקט ב JSX בניגוד ל HTML
דפדפנים לא מבינים את JSX בדיפולט, אז רוב משתמשי React מסתמכים על compiler כמו Babel או TypeScript כדי להפוך קוד JSX ל-JavaScript רגיל.
ערכות כלים רבות שהוגדרו מראש כמו Create React App או Next.js כוללות גם טרנספורמציה של JSX מתחת למכסה המנוע.
לא אתעמק בגרסאות חדשות יותר של JSX אבל בעבר כאשר המהדר של JSX היה נתקל בקוד JSX למשל
return <h1>Hello World</h1>;
ה JSX יבצע transform לקוד
return React.createElement('h1', null, 'Hello world');
התהליך הזה נקרא Transpiration והיום הוא עובד קצת שונה אבל אין צורך להעמיק לצורך הסיכום הזה.
רכיבי React משתמשים ב props כדי לתקשר אחד עם השני. כל רכיב הורה יכול להעביר מידע מסוים לרכיבי הילד שלו על ידי מתן props.
props עשויים להזכיר לך תכונות HTML, אבל אתה יכול להעביר דרכם כל ערך JavaScript, כולל אובייקטים, מערכים ופונקציות.

לשים לב שחייב את הסוגריים המסולסלות בהעברת ערך.
כדי לקרוא את ה props בקומפוננטת אוואטר נוכל פשוט לרשום את השמות של הערכים שהעברנו אותם באופן הבא
function Avatar({person, size})
אפשר גם להעביר באופן הבא
function Avatar(props) {
let person = props.person
let size = props.size
}
בעצם props זה הפרמטר היחידים שקומפוננטה מקבלת והיא מכילה בתוכה עוד מידע שלרוב לא צריך, לכן הרבה פעמים מעבירים בצורה הראשונה ולא בשנייה.
אם מצהירים בצורה הראשונה אפשר גם לתת ערך דיפולטי
function Avatar({person, size = 100})
הסינתקס הזה שבוא מחליפים את הפרמטר props ב {var1, var2} נקרא destructing וזה שקול לקריאת הפרמטרים מהprops
לפעמים העברה יכולה לחזור על עצמה
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
לפעמים נרצה לחסוך את הboiler plate הנ״ל ולשם כך נוכל להשתמש בסינתקס של JSX שנקרא spread. הוא נראה כך
function Profile(props) {
return (
<div className = "card">
<Avatar {...props} />
</div>
)
}
כאשר מבצעים קינון של תגיות למשל
<Card>
<Avatar />
</Card>
האבא Card מקבל את המידע הזה כחלק מהprops תחת השם children.
מה שקורה כאן זה שCard קיבל כפרמטר את הקומפוננטה Avatar ובהגדרת הקומפוננטה Card עלינו להגדיר כיצד צריך לרנדר אותה.
כעת בקומפוננטה Card עצמה נבנה את המימוש
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}

if להחזיר ביטוי JSX מסויים.cond מתקיים אז תרנדר את A אחרת תרנדר את B.cond מתקיים תרנדר את A אחרת כלום.נניח שיש לנו רשימה של תוכן
<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>
ההבדל היחיד בין אלמנטים ברשימה הוא המידע שהם מכילים בתוכם.
JSX הוא כלי טוב כדי להשתמש במבנה נתונים דינמי כמו רשימה ואובייקטים של JS , ביחד עם פונקציות על אוספים כמו map,filter כדי להמיר רשימות לקומפוננטות.
עבור המערך הבא
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
נוכל להמיר לקומפוננטה כך
export default function List() {
const listItems = people.map(person =>
<li>{person}</li>
);
return <ul>{listItems}</ul>;
}
נשים לב שנקבל את ההערה הבאה:
Warning: Each child in a list should have a unique “key” prop.
כדי לטפל בשגיאה סך הכל צריך להוסיף פרמטר key לכל li
<li key={person.id}>...</li>
חשוב לשים לב ש
נוכל גם לפלטר אלמנטים לפני שנציג אותם באופן הבא
const chemists = people.filter(person =>
person.profession === 'chemist'
);
ועל זה לעשות map כמו מקודם.
קומפוננטה חייבת להיות טהורה שזה אומר:
רינדור יכול לקרות בכל זמן, קומפוננטות לא צריכות להיות תלויות ברינדור של האחרת.
אסור לשנות שום קלט שהקומפוננטה משתמשת בו לרינדור, זה כולל props , states ו contexts. אם נרצה לעדכן את המסך נשתמש בפונקציות hooks עליהם נדבר בהמשך.
ננסה לבטא את הלוגיקה של הקומפוננטה בתוך ה JSX שאנחנו מחזירים. אם נרצה לשנות ערכים נרצה לעשות זאת עם event handlers או במוצא אחרון להשתמש ב useEffect שגם עליו נדבר.
זה בסדר לשנות משתנים שנוצרות תוך כדי הרינדור.
כמה דברים על המסך מתעדכנים בתגובה לקלט המשתמש. לדוגמה, לחיצה על גלריית תמונות משנה את התמונה הפעילה. ב-React, מידע שמשתנה עם הזמן נקרא State. אתה יכול להוסיף state לכל קומפוננטה, ולעדכן אותו לפי הצורך.
React מאפשרת הוספת handlers עם JSX.
אומנם יש את הקומפוננטות המובנות שתומכות רק ב events מובנים אבל נוכל ליצור קומפוננטות משלנו וליצור להם events handlers ולהעביר אותם כ props. לקומפוננטות המובנות
export default function App() {
return (
<Toolbar
onPlayMovie={() => alert('Playing!')}
onUploadImage={() => alert('Uploading!')}
/>
);
}
function Toolbar({ onPlayMovie, onUploadImage }) {
return (
<div>
<Button onClick={onPlayMovie}>
Play Movie
</Button>
<Button onClick={onUploadImage}>
Upload Image
</Button>
</div>
);
}
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
נשים לב שאנחנו מעבירים את הפונקצייה עצמה כ props ולא מפעילים אותה

event handlers יתפסו events מכל הילדים שהקומפוננטה שלך שהקומפוננטה עשויה להכיל. מונח זה נקרא event propagation , הוא גורם למאורעות לעלות למעלה בעץ הירכיית הקומפוננטות (זה נכון גם ל HTML ) .
למשל בקוד הבא אם נלחץ על הכפתור יופעל גם ה onclick של ה div
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<button onClick={() => alert('Playing!')}>
Play Movie
</button>
<button onClick={() => alert('Uploading!')}>
Upload Image
</button>
</div>
);
}
אם נרצה למנוע את זה נשתמש בפרמטר שעובר ב events ונפעיל עבורו את הפוקנצייה event.stopPropagation
למרות שאמרנו שקומפוננטות צריכות להיות טהורות, event handlers הם מקום טוב מאוד לשנות ערכים ומשתנים ששמורים בstate
רכיבים צריכים לעתים קרובות לשנות את מה שמופיע על המסך כתוצאה מאינטראקציה. הקלדה בטופס אמורה לעדכן את שדה הקלט, לחיצה על "הבא" בקרוסלת תמונה אמורה לשנות איזו תמונה מוצגת, לחיצה על "קנה" מכניסה מוצר לסל הקניות. רכיבים צריכים "לזכור" דברים: ערך הקלט הנוכחי, התמונה הנוכחית, עגלת הקניות.
ב-React, סוג זה של זיכרון ספציפי לרכיב נקרא state.
אפשר להוסיף state לקומפוננטה עם useState . זה בעצם חלק מסדרת פונקציות מיוחדות שנקראות Hooks שנותנות לקומפוננטות שלנו גישה לפיצ׳רים של React כאשר state הוא אחד מהם.
useState נותן לנו להגדיר משתנה state בהינתן מצב התחלתי קריאה לuseState תחזיר זוג ערכים, בstate הנוכחי ו state setter שאיתה נוכל לעדכן את ה state.
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
בעצם useState מחזירה מערך בגודל 2 ואנחנו עושים לו destructing שזה פיצ׳ר של השפה JS שמאפשר לנו להפריד את איברי המערך למשתנים בשורת קוד אחת.
הנה דוגמה לשימוש בuseStates שהגדנו
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
const hasNext = index < sculptureList.length - 1;
function handleNextClick() {
if (hasNext) {
setIndex(index + 1);
} else {
setIndex(0);
}
}
function handleMoreClick() {
setShowMore(!showMore);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleNextClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<button onClick={handleMoreClick}>
{showMore ? 'Hide' : 'Show'} details
</button>
{showMore && <p>{sculpture.description}</p>}
<img
src={sculpture.url}
alt={sculpture.alt}
/>
</>
);
}
התוצאה נראת ככה:

כאשר נלחץ על הshowMore נשנה את הstate וזה יוביל לרנדור מחדש והתנאי יגרום לכך שנציג את ה<p> שכתבנו

לחיצה על next תשנה את האלמנט עליו אנחנו מסתכלים במערך שהבאנו

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

כאשר אפליקציית ה react שלנו נוצרת יש צורך לקרוא ל initial render. הרבה פעמים בframeworks השונים הקוד נכתב לבד או מוסתר מאיתנו אבל זה יקרה באופן הבא
import Image from './Image.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Image />);
בחלק מהframeworks שמאתחלות פרוייקט React נוכל לראות גם קוד המשתמש ב ReactDOM ישירות למשל
ReactDOM.render(
<MainComponent/>,
document.getElementById('root')
)
הרעיון הוא אותו דבר בעצם פשוט אנחנו משתמשים ישירות ב ReactDOM במקום לגשת ישירות לפונקציית createRoot שלו.
הפרמטר הראשון הוא הקומפוננטה והפרמטר השני הוא ה node ב DOM המקורי (תיכף נבדיל בין ReactDOM ל DOM) שנרצה לטעון אליו את הקומפוננטה.
לפני שReact נוצרה מפתחי web היו מבצעים מניפולציות על ה DOM ישירות באמצעות javascript או ספריות כמו jquery . התהליך היה יכול להיות מייגע ואיטי ולכן מפתחי React יצרו את ה virtual DOM ואת ה ReactDOM API. הראשון הוא עותק של ה DOM המקורי שמנוהל בצורה יותר יעילה ולאחר מכן משקף את התוצר על ה DOM המקורי.
השני הוא API שמאפשר לנו לתווך בין ה DOM ל גרסה הוירטואלית שלו בצורה שנוחה עבור המפתחים עם כמה פונקציות API פשוטות.
מה קורה ברינדור של קומפוננטה
מה קורה בקומיט של קומפוננטה לDOM
לאחר הרינדור למסך React תבצע מניפולציות על הDOM.
appendChild כדי להוסיף את כל הקודקודים הרלוונטים.בניגוד למשתנים רגילים בגאווה סקריפט, React state מתנהג יותר כמו snapshot.
שינוי שלו באמצעות פונקציית set , לא משנה את הערך ישירות , במקום זה היא מריצה את הרנדור מחדש.
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!
רק לאחר הרנדור מחדש המידע נשמר שכן הuseState נקרא מחדש בעת רנדור הקומפוננטה והערך ההתחלתי כעת יהיה הערך החדש.
ההתנהגות שתיארתי למעלה יכולה לגרום להתנהגות לא צפויה כאשר קוראים לשינוי state מספר פעמים ברצף.
import { useState } from 'react';
export default function Counter() {
const [score, setScore] = useState(0);
function increment() {
setScore(score + 1);
}
return (
<>
<button onClick={() => increment()}>+1</button>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<h1>Score: {score}</h1>
</>
)
}
בקומפוננטה הזאת לחיצה על הכפתור
הסיבה לכך היא ששינוי state הוא לא פעולה מידית ובעצם יש בקשה לרינדור מחדש מReact כפי שכבר ציינו.
רינדור זה בעצם React מפעילה את הפונקצייה שהיא הקומפוננטה שלך וה jsx שחוזר ממנה מהווה snapshot של הUI כאשר כל המידע שמתלווה אליו מחושב בהתאם ל state ברגע הרינדור.
בניגוד להרבה snapshots אחרים, snapshot של UI הוא אינטרקטיבי. הוא כולל בתוכו לוגיקה והאזנה למאורעות כדי לתאר מה קורה כאשר המשתמש מבצע דברים מסויימים.
כאשר React מבצעת רינדור מחדש:

בניגוד למשתנים מקומיים רגילים שנעלמים כי הם חיים רק ב function scope של הקומפוננטה, ה state נשמר על ידי React עצמה במאגר משלה, מחוץ לפונקצייה. כאשר React מפעילה את הפונקציה היא מתחשבת בערך החדש של ה state שנשמר על ידי react.

במילים אחרות שינוי state מכניס בקשה של רינדור מחדש עם הערך החדש של ה state הנוכחי עבור הקומפוננטה לתור של רינדורים מחדש.
ולכן: כאשר בקוד הנ״ל קראנו לsetScore 3 פעמים ברציפות בעצם העברנו את אותו ה snapshot לפני שהספיק להתבצע בכלל רינדור מחדש ולכן הערך יגדל רק ב
זה שקול ללבצע את הקוד
<button onClick={() => {
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
}}>+3</button>
דוגמה קלאסית לזה היא שימוש ב setTimeout עם פרמטר שמוחזר מstate
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setTimeout(() => {
alert(number);
}, 3000);
}}>+5</button>
</>
)
}
במצב זה לחיצה על הכפתור תעשה 2 דברים: היא תשלח בקשה לרינדור מחדש עם הערך num+5 אבל תכניס לאחר 3 שניות בקשה ל message queue עם הערך שהיה לפני הרינדור שהוא 0 ולכן התוצאה תהיה


בגלל איך ש Javascript עובדת, React מחכה עד שכל הקוד שבתוך ה event handler יסתיים לפני שהיא מבצעת את הרינדור מחדש ולכן הרינדור מחדש קורה רק אחרי שכל ה setState שלנו הופעל.
כמו מלצר שמחכה להזמנה מכל השולחן לפני שהוא הולך

כדי לפתור את הבעיה הנ״ל אפשר להעביר ל setState פונקצייה שמרנדרת את הstate החדש לפי קודמו למשל:
setNumber(n => n+1)
בעצם לא העברנו את הnumber המקומי שאנחנו משתמשים בו ב function scope אלא הבאנו לReact פונקצייה כיצד לחשב את הערך הבא עם ה snapshot ששמור לה במדף.
אם נקרא לזה 3 פעמים התור ייראה ככה:

כמובן שאפשר גם לייצר state על אובייקט
const [person, setPerson] = useState({
firstName: 'Barbara',
lastName: 'Hepworth',
email: 'bhepworth@sculpture.com'
});
שינוי של האובייקט ישירות למשל preson.firstName = saar לא יגרום לרינדור מחדש.
נוכל אם כן לשנות את האובייקט בכמה דרכים
setPerson({
...person, // Copy the old fields
firstName: e.target.value // But override this one
});
השיטה של spread תעבוד גם אם יש nested objects למשל:
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
})
const nextArtwork = { ...person.artwork, city: 'New Delhi' };
const nextPerson = { ...person, artwork: nextArtwork };
setPerson(nextPerson);
באופן דומה נוכל לבצע spread למערכים ולשנות ערכים בו או להוסיף אליו אלמנטים חדשים
setArtists( // Replace the state
[ // with a new array
...artists, // that contains all the old items
{ id: nextId++, name: name } // and one new item at the end
]
)

הפתרון הפשוט ביותר הוא פתרון שנקרא lift up state שאומר להעביר את הstate מקומפוננטת האב דרך הילדים באמצעות props.
import { useState } from 'react';
export default function Accordion() {
const [activeIndex, setActiveIndex] = useState(0);
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel
title="About"
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel
title="Etymology"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
function Panel({
title,
children,
isActive,
onShow
}) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={onShow}>
Show
</button>
)}
</section>
);
}
state מבודד בין הרכיבים. React עוקב אחר איזה state שייך לאיזה קומפוננטה על סמך מקומו בעץ ה-UI. נוכל לשלוט מתי לשמר את המצב ומתי לאפס אותו בין רינדור מחדש.
React מייצרת עץ מה JSX השונים ומעדכנת את ה DOM לאחר מכן

state , כפי שכבר ניתן היה להבין מההסברים הקודמים, לא חי בתוך הקומפוננטה אלא נשמר אצל React פר קומפוננטה.
לכן למשל הקוד הבא
import { useState } from 'react';
export default function App() {
const counter = <Counter />;
return (
<div>
{counter}
{counter}
</div>
);
}
function Counter() {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
let className = 'counter';
if (hover) {
className += ' hover';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
מחזיק 2 states שונים למרות ששמנו JSX אחד תחת const

העץ ייראה ככה:

אם כן , כעת כשאנחנו יודעים איך המנגנון עובד, נרשום מספר כללי אצבע לכל הקשור לשמירה ואתחול מחדש של ה state.
key וככה React יודעת להבדיל בין קומפוננטות באותו מיקום בעץ. אם היא מזהה שהמפתח השתנה , היא תרנדר מחדש.בדרך כלל נעביר מידע מקומפוננטת אבא לבן באמצעות props.
התהליך הזה טוב להרבה מקרים והוא נקרא state up.
אבל התרחיש שיכול להווצר מזה נקרא prop drilling שזה מצב שבוא מחלחלים את ה prop עמוק בתוך העץ עד שמשתמשים בו באמת.

קונטקסט הוא אלטרנטיבה טובה שפותרת את המצב הזה והיא טובה למצבים שבהם נרצה להעביר מידע כלשהו מהקומפוננטה הגבוהה ביותר ועד לנמוכות ביותר בעץ (למשל, user שמחובר כרגע לאפליקציה שלנו).
נרצה למשל להחליף מצב כזה
<Section>
<Heading level={3}>About</Heading>
<Heading level={3}>Photos</Heading>
<Heading level={3}>Videos</Heading>
</Section>
במצב שבו מעבירים את הprop רק ל Section והילדים מצליחים להשתמש בו בלי לקבל אותו
<Section level={3}>
<Heading>About</Heading>
<Heading>Photos</Heading>
<Heading>Videos</Heading>
</Section>
במימוש הנוכחי זה לא יעבוד אבל עם Context נוכל לגרום לזה לקרות.
יצירת קובץ חדש וייצור שלו החוצה.
import { createContext } from 'react';
export const LevelContext = createContext(1);
ה createContext מקבל ערך התחלתי של 1.
לייבא את הקונטקסט
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
להוציא את ה level מה props של Heading ונקרא אותו כך
export default function Heading({ children }) {
const level = useContext(LevelContext);
// ...
}
useContext זה hooks בידיוק כמו useState ולכן אפשר לקרוא לו רק בתוך הקומפוננטה.
כעת נוכל לשלוף את הcontext אבל יהיה לו ערך התחלתי של 1 תמיד.
כל שנשאר לעשות הוא שיהיה ניתן לשנות את הcontext ממקומות מסויימים לאורך הקומפוננטות שלנו בעץ זה נעשה עם provider
import { LevelContext } from './LevelContext.js';
export default function Section({ level, children }) {
return (
<section className="section">
<LevelContext.Provider value={level}>
{children}
</LevelContext.Provider>
</section>
);
}
זה אומר ל React ש ״אם מישהו מנסה לגשת ל LevelContext בתוך ה Section, תספק לו את הערך של ה level המתקבל ב props.
וכעת המידע באמת יעבור לילדים בצורה נכונה
import Heading from './Heading.js';
import Section from './Section.js';
export default function Page() {
return (
<Section level={1}>
<Heading>Title</Heading>
<Section level={2}>
<Heading>Heading</Heading>
<Heading>Heading</Heading>
<Heading>Heading</Heading>
<Section level={3}>
<Heading>Sub-heading</Heading>
<Heading>Sub-heading</Heading>
<Heading>Sub-heading</Heading>
<Section level={4}>
<Heading>Sub-sub-heading</Heading>
<Heading>Sub-sub-heading</Heading>
<Heading>Sub-sub-heading</Heading>
</Section>
</Section>
</Section>
</Section>
);
}

כאשר נרצה שקומפוננטה תזכור מידע מבלי לרנדר אותו מחדש נשתמש ב useRef
const ref = useRef(0)
ref הוא אובייקט מהצורה {current:0}. הערך הזה הוא mutable , וזה בכוונה ככה, React לא עוקבת אחריו כלומר זה מעין אובייקט ששמור לצורך שמירת מידע בזמן מחזור החיים של הקומפוננטה. כשהיא תרונדר מחדש לא נקבל את הערך האחרון שעבדנו איתו.
ישנם מספר הבדלים שחשוב לציין:

יש לuseRef מספר שימושים עיקריים:
ref ואז להפעיל לcurrent החוזר ממנה פונקצייה הנקראת focusimport { useRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function MyForm() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
בשיטה הזאת נוכל להפעיל focus על אלמנט כלשהו ב DOM מבלי שהיוזר ייגע בו ישירות.
כדי להבין כיצד להשתמש בהוק React useEffect, חשוב להבין תחילה מה הוא עושה. ה-useEffect Hook ב-React מאפשר לך לבצע תופעות לוואי ברכיבים פונקציונליים. תופעות הלוואי יכולות לכלול שליפת נתונים מ-API, הרשמה לאירועים, מניפולציה של ה-DOM ועוד.
כפי שאמרנו נשתמש בו כאשר לא נדע בידיוק מיהו הevent שאפשר לשייך למידע שלנו. המקרה הקלאסי הוא מצב שבוא נרצה לעשות משהו ברגע שהקומפוננטה נטענת למסך.
מסיבה זאת זה גם נקרא useEffect, כי אנחנו מצמידים למידע מסויים side effect שקורה ברגע שהוא משתנה.
כדי לייצר effect נלך על השלבים הבאים
יצירת האפקט
import { useEffect } from 'react';
לקרוא לו בתוך הקומפוננטה הרלוונטית
function MyComponent() {
useEffect(() => {
// Code here will run after *every* render
});
return <div />;
}
במצב זה האפקט ייקרא לאחר שהקומפוננטה תתרנדר למסך (גם ברנדור התחלתי וגם בכל רנדור מחדש).
בגלל שאפקטים נקראים לאחר כל רנדור , קוד כמו זה יגרום ללולאה אינסופית

אם נרצה להפעיל את האפקט רק ברנדור ההתחלתי נוכל להעביר לאפקט שלנו מערך ריק למשל
useEffect(() => {
// This will only run on initial render
fetchData().then(response => setData(response));
}, []);
החסרון בשיטה הזו היא שברגע שמשתמשים ב dependency בתוך האפקט נצטרך להצמיד אותו למערך למשל
useEffect(() => {
if (isPlaying) { // It's used here...
// ...
} else {
// ...
}
}, [isPlaying]); // ...so it must be declared here!
במצב זה נריץ את האפקט כל פעם שisPlaying משתנה בין רנדורים.
cleanup function
נוכל להוסיף לוגיקה שמבצעת ניקוי של משאבים מתוך האפקט לפני שהוא רץ מחדש ופעם אחרונה כשהקומפוננטה יוצאת מההירכייה
useEffect(() => {
const connection = createConnection();
connection.connect();
return () => {
connection.disconnect();
};
}, []);
כפי שכבר ראינו ניתן לשלב CSS ו React על ידי העברת פרמטר style עם אובייקט js כ prop.
ברוב המקרים זה לא יספיק לנו אבל נרצה לספק stylesheet לקומפוננטה שלנו. לשם כך נייצר קובץ css מתאים ופשוט נייבא אותו.
למשל נייצר קובץ שנקרא `App.css
body {
background-color: #282c34;
color: white;
padding: 40px;
font-family: Arial;
text-align: center;
}
ופשוט נייבא אותו היכן שנרצה
import React from 'react';
import ReactDOM from 'react-dom/client';
import './App.css';
class MyHeader extends React.Component {
render() {
return (
<div>
<h1>Hello Style!</h1>
<p>Add a little style!.</p>
</div>
);
טעינה של assets יעבוד באופן דומה לטעינה של קובץ css

ניווט בין דפים מבוסס URL במקום state.
// App.js
import { Routes, Route } from 'react-router-dom';
import Home from './Pages/Home';
import About from './Pages/About';
import Products from './Pages/Products';
const App = () => {
return (
<>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/about" element={<About />} />
</Routes>
</>
);
};
export default App;
תחילה היינו יוצרים את הרכיבים הללו, במקרה שלנו שלושה דפים: דף הבית, דף אודות ודף המוצרים. מאגר GitHub זה מכיל את התוכן של דפים אלה. לאחר שעמודים אלו מוגדרים כהלכה, נוכל כעת להגדיר ולהגדיר את המסלולים שלנו בקובץ App.js, המשמש כבסיס לאפליקציית React שלנו.
השימוש הראשון אם כן הוא שייוך באמצעות URL אבל נוכל לבצע שיוך באמצעות תפריט למשל
// App.js
import NavBar from './Components/NavBar';
const App = () => {
return (
<>
<NavBar />
<Routes>
// ...
</Routes>
</>
);
};
export default App;
// Components/NavBar.js
import { Link } from 'react-router-dom';
const NavBar = () => {
return (
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/products">Products</Link>
</li>
</ul>
</nav>
);
};