Menu

שאלות ראיון עבודה ב-React עם תשובות (React 19)

כ-30 שאלות ראיון ב-React מרמת מתחילים ועד מתקדמים, כל אחת עם תשובה קצרה שמראיין היה מקבל, וקוד חי לשאלות שקל יותר לראות מאשר לקרוא.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

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

לפני שאתם לוחצים: מה הקונסול מתחת לתצוגה המקדימה מדפיס, ומה הכפתור מציג? התשובה נמצאת בחלק של המתחילים שמתחת.

שאלות למתחילים

מה זה React?

React היא ספריית JavaScript לבניית ממשקי משתמש מקומפוננטות. כותבים פונקציות שמחזירות תיאור של ה-UI עבור הנתונים הנוכחיים, וכשהנתונים משתנים React מחשבת מה השתנה ומעדכנת בשבילכם את ה-DOM. היא מטפלת רק בשכבת התצוגה: ניתוב, טעינת נתונים וטפסים מגיעים מספריות אחרות או מ-framework כמו Next.js.

מה זה JSX?

JSX הוא הרחבת תחביר שמאפשרת לכתוב markup בתוך JavaScript. קומפיילר (Babel, esbuild, SWC) הופך כל תגית לקריאה שיוצרת React element, כך ש-<p className="note">{text}</p> הופך לאובייקט עם type: 'p' ועם ה-props. מכיוון שזה JavaScript, משתמשים ב-className במקום class, שמים ביטויים בתוך {}, וכל תגית חייבת להיסגר. הדף על JSX מכסה את הכללים בפירוט.

מה ההבדל בין קומפוננטה לאלמנט?

קומפוננטה היא פונקציה (או class) שמקבלת props ומחזירה UI. אלמנט הוא האובייקט הפשוט ש-JSX מייצר, שמתאר מה לרנדר: <Avatar size={40} /> הוא אלמנט שה-type שלו הוא הפונקציה Avatar. React קוראת לקומפוננטה שלכם כדי להפוך אלמנטים לעוד אלמנטים, עד למטה לתגיות DOM. אלמנטים זולים ו-immutable; אתם אף פעם לא קוראים לקומפוננטה בעצמכם.

מה ההבדל בין props ל-state?

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

מה הדוגמה הראשונה בדף הזה רושמת?

היא רושמת logged: 0, והכפתור מציג Count: 1, לא 2. state הוא תמונת מצב: count הוא קבוע לאורך כל הרינדור, ולכן שתי הקריאות setCount(count + 1) מחשבות 0 + 1, והלוג קורא את הערך הישן כי החדש קיים רק ברינדור הבא. כדי להוסיף שניים, השתמשו בצורת ה-updater: setCount((c) => c + 1) פעמיים.

למה רשימות צריכות keys, ולמה ה-index הוא key גרוע?

React משתמשת ב-key כדי להתאים כל פריט ברשימה לפריט מהרינדור הקודם. אם משתמשים ב-index של המערך ומכניסים פריט בראש הרשימה, כל index זז, ולכן React שומרת את ה-state של כל שורה במיקום הישן שלה ומחברת אותו לנתונים הלא נכונים.

סמנו את Ada, ואז לחצו על "Add to top": הסימון קופץ לשורה הראשונה החדשה. שנו את key={index} ל-key={person.id} ונסו שוב, והסימון נשאר עם Ada. בפיתוח React גם מזהירה כשחסרים keys; התצוגה המקדימה רצה כמו build של פרודקשן, אז לא תראו כאן את האזהרה הזו.

מה זה שדה מבוקר (controlled input)?

שדה שהערך שלו מגיע מ-state של React: מעבירים value ומעדכנים את ה-state ב-onChange, כך ש-React היא מקור האמת היחיד ואפשר לבצע ולידציה או לשנות כל הקשה. שדה לא מבוקר שומר את הערך שלו ב-DOM ואתם קוראים אותו כשצריך, דרך ref או דרך ה-FormData של הטופס. שדות מבוקרים הם בחירת ברירת המחדל כשה-UI מגיב למה שהמשתמש מקליד.

מה פירוש הרמת state למעלה?

כששתי קומפוננטות צריכות את אותם נתונים, מעבירים את ה-state להורה המשותף הקרוב ביותר שלהן ומעבירים אותו מטה כ-props, יחד עם פונקציה לשנות אותו. הילדים מפסיקים להחזיק עותקים משלהם, כך שהם אף פעם לא יכולים לא להסכים. זו התשובה הסטנדרטית ל-"איך קומפוננטות אחיות משתפות state" לפני שפונים לקונטקסט או ל-store.

מה הם כללי ההוקים?

קוראים להוקים רק ברמה העליונה של קומפוננטה או של הוק מותאם, אף פעם לא בתוך תנאים, לולאות או פונקציות מקוננות, ורק מפונקציות של React, לא מפונקציות JavaScript רגילות. React מזהה כל הוק לפי סדר הקריאות, ולכן הסדר הזה חייב להיות זהה בכל רינדור. החבילה eslint-plugin-react-hooks אוכפת את שני הכללים.

מה זה fragment?

fragment מקבץ כמה אלמנטים בלי להוסיף צומת DOM: <>...</>, או <Fragment key={id}> כשצריך key בתוך רשימה. קומפוננטה חייבת להחזיר שורש אחד, ו-fragments מאפשרים לשורש הזה להיות כמה אחים, מה שחשוב בתוך טבלאות ו-layouts של flex שבהם div נוסף היה שובר את ה-markup.

שאלות ברמת ביניים

איך מערך התלויות של useEffect עובד?

React מריצה את האפקט אחרי שהרינדור עבר commit (בדרך כלל אחרי שהדפדפן צייר), ואז שוב אחרי כל רינדור מאוחר יותר שבו ערך במערך השתנה (בהשוואה עם Object.is). בלי מערך הוא רץ אחרי כל רינדור; עם [] הוא רץ פעם אחת אחרי הרינדור הראשון. כל ערך מהקומפוננטה שהאפקט קורא שייך למערך, וה-linter אומר לכם כשאחד חסר.

מתי ה-cleanup של אפקט רץ?

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

לחצו על "Switch room" ושימו לב לקונסול: ה-cleanup של החדר הישן רץ קודם, ואז האפקט מתחבר לחדש. "Hide chat" מריץ רק את ה-cleanup. בפיתוח, StrictMode מוסיף ב-mount עוד connect, disconnect, connect כדי לבדוק שה-cleanup שלכם עובד; התצוגה המקדימה רצה כמו build של פרודקשן, אז רואים רק את הרצף האמיתי.

למה ה-state שלי נראה ישן בתוך setInterval?

ה-callback של ה-interval הוא closure על ה-count מהרינדור שיצר אותו. עם מערך תלויות ריק האפקט אף פעם לא רץ שוב, ולכן ה-callback ממשיך לקרוא את ה-count הראשון לנצח.

broken מגיע ל-1 ונעצר, כי כל tick מחשב 0 + 1. fixed ממשיך לספור, כי פונקציית ה-updater מקבלת את הערך העדכני. הוספת broken למערך התלויות גם עובדת, במחיר של ניקוי ויצירה מחדש של ה-interval בכל שנייה.

מה זה batching?

React מקבצת כמה עדכוני state לרינדור יחיד. מאז React 18 זה חל בכל מקום: event handlers, timeouts, promises ו-event listeners נייטיב. אם צריך שה-DOM יתעדכן מיד אחרי עדכון אחד, flushSync מ-react-dom מבטל את זה, אבל לעיתים רחוקות צריך אותו.

הלחיצה רושמת render 1 1 1 אחד, לא שלושה רינדורים. שלושה עדכוני state בתוך timeout היו פעם שלושה רינדורים לפני React 18.

מה זה רושם? (סדר האפקטים)

קראו את הקוד וכתבו את ארבע השורות שהקונסול ידפיס, לפי הסדר, לפני שאתם מסתכלים עליו.

הרינדור הולך מלמעלה למטה: render App, ואז render Child. אפקטים רצים מלמטה למעלה, אחרי שכל העץ עבר commit: effect Child, ואז effect App. האפקט של הורה יכול לסמוך על כך שהאפקטים של הילדים שלו כבר רצו.

מה ההבדל בין useMemo ל-useCallback?

useMemo(() => compute(a, b), [a, b]) שומר ב-cache את התוצאה של חישוב. useCallback(fn, [deps]) שומר את הפונקציה עצמה, ולכן הוא זהה ל-useMemo(() => fn, [deps]). שניהם עוזרים רק כשמשהו בהמשך משווה לפי הפניה: ילד עם memo, או מערך תלויות של הוק אחר. מאז React Compiler 1.0 (אוקטובר 2025) שלב build יכול להוסיף את ה-memoization הזה אוטומטית, אבל memoization ידני עדיין עובד ועדיין תראו אותו ברוב בסיסי הקוד.

מה React.memo עושה, ולמה הוא צריך לעיתים קרובות useCallback?

memo(Component) מדלג על רינדור מחדש של הקומפוננטה כשה-props שלה זהים לפעם הקודמת, בהשוואה אחד אחד עם Object.is. פונקציה שנכתבת inline בהורה היא פונקציה חדשה בכל רינדור, ולכן היא נחשבת ל-prop שהשתנה ומכשילה את memo. useCallback שומר את אותה פונקציה בין רינדורים.

נקו את הקונסול ולחצו על "Re-render parent": מופיע רק render inline handler. הילד השני קיבל את אותה פונקציה ודילגו עליו. הסירו את memo(...) מסביב ל-Button ושניהם רושמים בכל לחיצה, מה שמראה ש-useCallback לבד לא עושה כלום.

מה זה קונטקסט, ומה המחיר שלו?

קונטקסט מעביר ערך לכל קומפוננטה מתחת ל-provider בלי להשחיל אותו דרך props: createContext, ואז <ThemeContext value={theme}> ב-React 19 (או <ThemeContext.Provider>), ו-useContext(ThemeContext) כדי לקרוא אותו. המחיר הוא שכל קומפוננטה שקוראת את הקונטקסט מתרנדרת מחדש כשהערך משתנה, ו-memo לא עוצר את זה. שמרו על זה זול על ידי פיצול ערכים לא קשורים לקונטקסטים נפרדים, עטיפת ערך שהוא אובייקט ב-useMemo, והרחקת state שמשתנה מהר מקונטקסט ברמת האפליקציה. ראו useContext.

מה הם refs, ומתי משתמשים ב-ref במקום ב-state?

useRef(initial) מחזיר אובייקט שהמאפיין current שלו נשמר בין רינדורים, ושינוי שלו לא מפעיל רינדור. השתמשו ב-ref לערכים שהמסך לא תלוי בהם (מזהה טיימר, הערך הקודם, דגל) ולגישה ל-DOM (<input ref={inputRef}> ואז inputRef.current.focus()). אם הערך מוצג על המסך, הוא שייך ל-state.

מה זה ה-virtual DOM, ומה זה reconciliation?

בכל רינדור React בונה עץ חדש של אלמנטים ומשווה אותו לקודם; ההשוואה הזו היא reconciliation. אם הטיפוס של אלמנט השתנה (div הפך ל-section, או ProfileA הפכה ל-ProfileB), React הורסת את תת העץ הזה ואת ה-state שלו ובונה חדש. אם הטיפוס זהה, היא שומרת את צומת ה-DOM ומעדכנת את ה-props שהשתנו, והיא משתמשת ב-keys כדי להתאים ילדים ברשימות. "Virtual DOM" הוא השם הפופולרי לעץ הזה שבזיכרון; המהירות מגיעה מנגיעה רק במה שהשתנה, לא מכך שהעץ עצמו מהיר.

מה זה הוק מותאם אישית?

פונקציה ששמה מתחיל ב-use ושקוראת להוקים אחרים, כדי שאפשר יהיה להשתמש שוב בלוגיקה עם state בין קומפוננטות: useOnlineStatus, useLocalStorage, useDebouncedValue. כל קומפוננטה שקוראת לו מקבלת state עצמאי משלה; הוק מותאם משתף לוגיקה, לא נתונים. ראו הוקים מותאמים אישית.

מה StrictMode עושה?

<StrictMode> מפעיל בדיקות נוספות בפיתוח בלבד. הוא מרנדר קומפוננטות פעמיים כדי לחשוף רינדור לא טהור, מריץ setup, cleanup ו-setup של אפקטים פעם אחת ב-mount כדי לחשוף cleanup חסר, עושה אותו דבר ל-ref callbacks, ומזהיר על APIs מיושנים. שום דבר מזה לא קורה ב-build של פרודקשן, כך שלוג כפול בקונסול בפיתוח הוא צפוי ולא באג.

שאלות מתקדמות

מה זה רושם? (render, layout effect, effect)

עוד ניחוש אחד. שלושה לוגים, שלושה רגעים שונים ברינדור.

הסדר הוא render, אחר כך useLayoutEffect, ואז useEffect, למרות ש-useEffect כתוב ראשון. layout effects רצים אחרי ש-React מעדכנת את ה-DOM אבל לפני שהדפדפן מצייר, ולכן הם מתאימים למדידת אלמנט; אפקטים רגילים רצים מאוחר יותר, בדרך כלל אחרי הציור, והם צריכים להיות ברירת המחדל.

מה זה error boundary?

קומפוננטת class שתופסת שגיאות שנזרקות בזמן רינדור הילדים שלה ומציגה fallback במקום לעשות unmount לכל האפליקציה. היא צריכה static getDerivedStateFromError (כדי לעבור ל-fallback) ובדרך כלל componentDidCatch (כדי לרשום). עדיין אין מקבילה בהוקים, ולכן בבסיסי קוד של קומפוננטות פונקציה צוותים כותבים class אחד או משתמשים בחבילה react-error-boundary.

לחצו על "Break it": ה-fallback מחליף רק את הילדים של ה-boundary, הכפתור נשאר, ו-React עדיין מדווחת על השגיאה שנתפסה בקונסול. boundaries לא תופסים שגיאות ב-event handlers, בקוד אסינכרוני כמו setTimeout, או ב-boundary עצמו. בשבילם משתמשים ב-try/catch וקובעים state.

מה זה portal?

createPortal(children, domNode) מ-react-dom מרנדר ילדים לתוך צומת DOM אחר, בדרך כלל document.body, ושומר אותם באותו מקום בעץ של React. מודאלים, tooltips ו-toasts משתמשים בו כדי לברוח מ-overflow: hidden או מ-stacking context של הורה. קונטקסט עדיין עובד דרך portal, ואירועים עולים דרך העץ של React, כך שלחיצה בתוך ה-portal מגיעה ל-onClick של ההורה למרות שצמתי ה-DOM נמצאים במקום אחר. ראו portals.

מה הם Suspense ו-lazy?

lazy(() => import('./Chart')) טוען את הקוד של קומפוננטה בפעם הראשונה שהיא מתרנדרת, מה שמפצל את ה-bundle שלכם. <Suspense fallback={<Spinner />}> מציג את ה-fallback כל עוד משהו בתוכו עדיין נטען: קוד lazy, או נתונים שנקראים עם use(promise) או עם framework שתומך ב-Suspense. מיקום הגבולות קובע אילו חלקים בדף מחכים יחד.

import { lazy, Suspense } from 'react';

const Chart = lazy(() => import('./Chart.jsx'));

export default function Dashboard() {
    return (
        <Suspense fallback={<p>Loading chart...</p>}>
            <Chart />
        </Suspense>
    );
}

מה useTransition עושה?

הוא מסמן עדכון state כלא דחוף. const [isPending, startTransition] = useTransition() ו-startTransition(() => setTab('posts')) מאפשרים ל-React לשמור על המסך הנוכחי רספונסיבי ולקטוע את הרינדור האיטי אם מגיע עדכון דחוף יותר, כמו הקשה. isPending אומר לכם להציג רמז. ב-React 19 הפונקציה שמעבירים יכולה להיות async, ולזה React קוראת action. אל תשתמשו בו כדי לעדכן את הערך של שדה טקסט עצמו; העדכון הזה חייב להיות דחוף.

מה הם React Server Components?

קומפוננטות שרצות רק בשרת (או בזמן ה-build) ושולחות לדפדפן את התוצאה המרונדרת שלהן, אף פעם לא את הקוד שלהן. הן יכולות להיות async ולקרוא ממסד נתונים או ממערכת הקבצים ישירות, אבל הן לא יכולות להשתמש ב-state, באפקטים או ב-APIs של הדפדפן. קובץ שמסומן ב-'use client' פותח גבול לקוח שבו חיות קומפוננטות אינטראקטיביות, ו-'use server' מסמן פונקציות שרת שקוד לקוח יכול לקרוא להן. משתמשים בהן דרך framework כמו Next.js עם ה-App Router; לאפליקציות Vite רגילות אין שרת שיריץ אותן.

מה השתנה ב-React 19?

  • Actions: פונקציות async בתוך transitions, עם מצב pending שמטופל בשבילכם. <form action={fn}> קורא ל-fn עם ה-FormData של הטופס.
  • הוקים חדשים: useActionState בשביל התוצאה ומצב ה-pending של טופס, useOptimistic בשביל UI אופטימי, ו-useFormStatus ב-react-dom.
  • use: קורא promise (ועושה suspend עד שהוא מסתיים) או קונטקסט, ובניגוד להוקים אחרים אפשר לקרוא לו בתוך תנאי.
  • ref כ-prop: קומפוננטות פונקציה מקבלות ref כמו כל prop אחר, כך ש-forwardRef כבר לא נחוץ לקוד חדש. ref callbacks יכולים להחזיר פונקציית cleanup.
  • <Context> כ-provider, ועוד תגיות <title> ו-<meta> שעולות ל-head של המסמך.
  • הסרות: ReactDOM.render, string refs, legacy context, ו-defaultProps בקומפוננטות פונקציה (השתמשו בפרמטרים עם ערכי ברירת מחדל).
// React 19: ref is a normal prop
function TextInput({ ref, ...props }) {
    return <input ref={ref} {...props} />;
}

בדף על React 19 יש דוגמאות שרצות לכל אחד מאלה.

איך בודקים קומפוננטות React?

עם React Testing Library ו-runner כמו Vitest או Jest. מרנדרים את הקומפוננטה, מוצאים אלמנטים כמו שמשתמש היה מוצא (לפי role, תווית או טקסט), מבצעים אינטראקציה עם user-event, ובודקים את מה שגלוי, לא state פנימי. זה שומר על הבדיקות תקפות לאורך refactors.

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Counter from './Counter';

test('counts clicks', async () => {
    const user = userEvent.setup();
    render(<Counter />);
    await user.click(screen.getByRole('button', { name: /clicked 0 times/i }));
    expect(screen.getByRole('button')).toHaveTextContent('Clicked 1 times');
});

toHaveTextContent מגיע מ-@testing-library/jest-dom. לזרימות שלמות בין דפים, צוותים מוסיפים מעל זה Playwright או Cypress.

איך מוצאים ומתקנים בעיית ביצועים?

קודם מודדים. פתחו את הטאב Profiler ב-React DevTools, הקליטו את האינטראקציה האיטית, וקראו את ה-flame chart: הוא מראה אילו קומפוננטות התרנדרו, כמה זמן לקחה כל אחת, ו(עם ההגדרה "record why each component rendered") למה. תיקונים טיפוסיים, בסדר שכדאי לנסות אותם: להוריד state למטה כדי שפחות קומפוננטות יהיו תלויות בו, להעביר תתי עצים איטיים בתור children, לעשות memoization לחלק היקר עם memo, useMemo ו-useCallback, לעשות וירטואליזציה לרשימות ארוכות, ולדחות עדכונים כבדים עם useTransition או useDeferredValue. עשו profiling ב-build של פרודקשן או של profiling; builds של פיתוח איטיים יותר ומגזימים בזמני הרינדור.

למה קומפוננטה צריכה להיות טהורה?

בהינתן אותם props, state וקונטקסט, קומפוננטה צריכה להחזיר את אותו JSX ולא לשנות שום דבר מחוצה לה בזמן הרינדור. React עשויה לרנדר קומפוננטה יותר מפעם אחת, שלא לפי הסדר, או לזרוק רינדור (transitions, Suspense, StrictMode), כך שתופעות לוואי בגוף הקומפוננטה, כמו שינוי משתנה או שליחת בקשה, רצות מספר בלתי צפוי של פעמים. תופעות לוואי שייכות קודם כל ל-event handlers, ולאפקטים כששום אירוע לא גרם להן.

מתי לא כדאי להשתמש באפקט?

כשאפשר לחשב את הערך בזמן הרינדור (רשימות מסוננות, שמות מלאים, סכומים), כשהעבודה נגרמת מפעולת משתמש (שימו אותה ב-event handler), וכשרוצים לאפס state כש-prop משתנה (השתמשו ב-key). אפקטים נועדו לסנכרון עם משהו מחוץ ל-React: חיבור רשת, API של הדפדפן, widget חיצוני. שרשראות של אפקטים שקובעים state הן מקור נפוץ לרינדורים מיותרים ולבאגים.

שאלות נפוצות

מה זה React?

React היא ספריית JavaScript לבניית ממשקי משתמש מקומפוננטות. אתם מתארים איך המסך צריך להיראות עבור הנתונים הנוכחיים, ו-React מעדכנת את ה-DOM האמיתי כשהנתונים האלה משתנים.

מה ההבדל בין state ל-props?

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

מה הם הוקים ב-React?

הוקים הם פונקציות ששמן מתחיל ב-use ומאפשרות לקומפוננטת פונקציה להשתמש ביכולות של React: useState בשביל state, useEffect בשביל תופעות לוואי, useContext בשביל קונטקסט, useRef בשביל ערכים שלא מפעילים רינדורים, ועוד.

מה זה ה-virtual DOM?

זה השם לתיאור של ה-UI ש-React מחזיקה בזיכרון. בכל רינדור React בונה תיאור חדש, משווה אותו לקודם, ומחילה רק את ההבדלים על ה-DOM האמיתי. ההשוואה הזו נקראת reconciliation.

למה keys חשובים ברשימות ב-React?

key אומר ל-React איזה פריט ברשימה הוא איזה בין רינדורים. עם keys יציבים (כמו id) React שומרת את ה-state ואת צומת ה-DOM של כל פריט מחוברים לנתונים הנכונים כשפריטים מתווספים, מוסרים או משנים סדר. שימוש ב-index של המערך שובר את זה כשהסדר משתנה.

מה זה JSX?

JSX הוא הרחבת תחביר שמאפשרת לכתוב markup שדומה ל-HTML בתוך JavaScript. קומפיילר הופך כל תגית לקריאה לפונקציה שיוצרת React element, כך ש-<h1>Hi</h1> הופך לאובייקט פשוט שמתאר h1.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל