هذه أسئلة مقابلات React الأكثر تكرارًا، مجمّعة حسب المستوى ومجاب عنها كما يتوقع المقابِل: باختصار، وبصحة بالنسبة لـ React 19، ومع السبب الكامن وراء القاعدة. وحيث تكون رؤية السلوك أفضل من القراءة عنه، يحتوي الجواب على محرر حي، وتطلب منك بعض الأسئلة توقع مخرجات وحدة التحكم قبل التشغيل.
قبل أن تنقر: ماذا تطبع وحدة التحكم أسفل المعاينة، وماذا يعرض الزر؟ الجواب في قسم المبتدئين أدناه.
أسئلة المستوى المبتدئ
ما هي React؟
React مكتبة JavaScript لبناء واجهات المستخدم من مكوّنات. تكتب دوالًا تُرجع وصفًا للواجهة للبيانات الحالية، وعندما تتغير تلك البيانات تحدد React ما الذي تغيّر وتحدّث الـ DOM نيابة عنك. وهي تتولى طبقة العرض فقط: يأتي التوجيه وجلب البيانات والنماذج من مكتبات أخرى أو من إطار عمل مثل Next.js.
ما هي JSX؟
JSX امتداد للصيغة يتيح لك كتابة الترميز داخل JavaScript. يحوّل المترجم (Babel أو esbuild أو SWC) كل وسم إلى استدعاء ينشئ عنصر React، فيصبح <p className="note">{text}</p> كائنًا فيه type: 'p' والـ props. ولأنها JavaScript، تستخدم className بدلًا من class، وتضع التعابير في {}، ويجب إغلاق كل وسم. تتناول صفحة JSX القواعد بالتفصيل.
ما الفرق بين المكوّن والعنصر؟
المكوّن دالة (أو صنف) تأخذ props وتُرجع واجهة. والعنصر هو الكائن العادي الذي تنتجه JSX، ويصف ما يُعرض: <Avatar size={40} /> عنصر نوعه type هو الدالة Avatar. تستدعي React مكوّنك لتحوّل العناصر إلى عناصر أخرى، وصولًا إلى وسوم DOM. العناصر رخيصة وغير قابلة للتعديل؛ ولا تستدعي مكوّنًا بنفسك أبدًا.
ما الفرق بين props والحالة؟
يمرر الأب props، وهي للقراءة فقط داخل المكوّن. أما الحالة فيملكها المكوّن، وتتغير بمرور الوقت عبر دالة ضبط، وكل تغيير يجدول عرضًا جديدًا. وطريقة شائعة لقول ذلك: props تُعدّ المكوّن، والحالة ذاكرته. ويمكن للقيمة نفسها أن تكون حالة في الأب وprop في ابنه.
ماذا يسجّل المثال الأول في هذه الصفحة؟
يسجّل logged: 0، ويعرض الزر Count: 1، لا 2. الحالة لقطة: count ثابت طوال العرض، فيحسب استدعاءا setCount(count + 1) كلاهما 0 + 1، ويقرأ السجل القيمة القديمة لأن الجديدة لا توجد إلا في العرض التالي. ولإضافة اثنين، استخدم صيغة دالة التحديث: setCount((c) => c + 1) مرتين.
لماذا تحتاج القوائم إلى مفاتيح، ولماذا يُعد الفهرس مفتاحًا سيئًا؟
تستخدم React الخاصية key لتطابق كل عنصر في القائمة مع نظيره من العرض السابق. إذا استخدمت فهرس المصفوفة وأدرجت عنصرًا في الأعلى، يتزحزح كل فهرس، فتُبقي React حالة كل صف في موضعه القديم وتربطها بالبيانات الخطأ.
حدّد Ada، ثم انقر "Add to top": تقفز علامة التحديد إلى الصف الأول الجديد. غيّر key={index} إلى key={person.id} وجرّب مرة أخرى، فتبقى العلامة مع Ada. وفي التطوير تحذّر React أيضًا عندما تكون المفاتيح مفقودة؛ والمعاينة تعمل مثل بناء الإنتاج، فلن ترى ذلك التحذير هنا.
ما هو الحقل المتحكم به؟
حقل تأتي قيمته من حالة React: تمرر value وتحدّث الحالة في onChange، فتكون React المصدر الوحيد للحقيقة ويمكنك التحقق من كل ضغطة مفتاح أو تحويلها. أما الحقل غير المتحكم به فيحتفظ بقيمته الخاصة في DOM وتقرؤها عند الحاجة، عبر مرجع أو FormData الخاص بالنموذج. الحقول المتحكم بها هي الخيار الافتراضي عندما تتفاعل الواجهة مع ما يكتبه المستخدم.
ماذا يعني رفع الحالة؟
عندما يحتاج مكوّنان إلى البيانات نفسها، تنقل الحالة إلى أقرب أب مشترك لهما وتمررها إلى الأسفل كـ props، مع دالة لتغييرها. يتوقف الأبناء عن امتلاك نسخهم الخاصة، فلا يمكن أن يختلفوا أبدًا. وهو الجواب المعتاد عن سؤال «كيف تتشارك المكوّنات الشقيقة الحالة» قبل اللجوء إلى السياق أو مخزن.
ما هي قواعد الخطافات؟
لا تستدعِ الخطافات إلا في المستوى الأعلى من مكوّن أو خطاف مخصص، ولا تستدعها أبدًا داخل شروط أو حلقات أو دوال متداخلة، ولا تستدعها إلا من دوال React، لا من دوال JavaScript عادية. تميّز React كل خطاف بترتيب الاستدعاءات، لذا يجب أن يكون ذلك الترتيب متطابقًا في كل عرض. وتفرض الحزمة eslint-plugin-react-hooks القاعدتين.
ما هو الجزء (fragment)؟
يجمع الجزء عدة عناصر دون إضافة عقدة DOM: <>...</>، أو <Fragment key={id}> عندما تحتاج إلى مفتاح داخل قائمة. يجب أن يُرجع المكوّن جذرًا واحدًا، وتتيح الأجزاء أن يكون ذلك الجذر عدة أشقاء، وهذا مهم داخل الجداول وتخطيطات flex حيث سيكسر div إضافي الترميز.
أسئلة المستوى المتوسط
كيف تعمل مصفوفة اعتماديات useEffect؟
تشغّل React التأثير بعد إثبات العرض (عادة بعد أن يرسم المتصفح)، ثم من جديد بعد أي عرض لاحق تغيّرت فيه قيمة في المصفوفة (بالمقارنة عبر Object.is). من دون مصفوفة يعمل بعد كل عرض؛ ومع [] يعمل مرة واحدة بعد العرض الأول. كل قيمة من المكوّن يقرؤها التأثير مكانها المصفوفة، وتخبرك أداة الفحص عندما تكون إحداها مفقودة.
متى تعمل دالة تنظيف التأثير؟
الدالة التي تُرجعها من التأثير تعمل قبل أن يعمل التأثير من جديد باعتماديات جديدة، ومرة واحدة عندما يُزال المكوّن. وهي المكان الذي تلغي فيه ما أعدّه التأثير: مسح مؤقت، أو إزالة مستمع، أو إغلاق اتصال.
انقر "Switch room" وراقب وحدة التحكم: يعمل تنظيف الغرفة القديمة أولًا، ثم يتصل التأثير بالجديدة. ولا يشغّل "Hide chat" إلا التنظيف. وفي التطوير يضيف StrictMode اتصالًا وقطعًا واتصالًا إضافيًا عند التركيب ليتحقق من أن تنظيفك يعمل؛ والمعاينة تعمل مثل بناء الإنتاج، فلا ترى إلا التسلسل الحقيقي.
لماذا تبدو حالتي قديمة داخل setInterval؟
دالة الاستدعاء الخاصة بالمؤقت إغلاق (closure) على count من العرض الذي أنشأها. ومع مصفوفة اعتماديات فارغة لا يعمل التأثير من جديد أبدًا، فتظل دالة الاستدعاء تقرأ count الأول إلى الأبد.
يصل broken إلى 1 ويتوقف، لأن كل نبضة تحسب 0 + 1. أما fixed فيستمر في العد، لأن دالة التحديث تستقبل أحدث قيمة. وإضافة broken إلى مصفوفة الاعتماديات تعمل أيضًا، لكن على حساب مسح المؤقت وإعادة إنشائه كل ثانية.
ما هو التجميع (batching)؟
تجمع React عدة تحديثات حالة في عرض واحد. ومنذ React 18 ينطبق هذا في كل مكان: معالجات الأحداث والمهل الزمنية والوعود ومستمعو الأحداث الأصلية. وإذا احتجت إلى تحديث DOM فورًا بعد تحديث واحد، فإن flushSync من react-dom يخرجك من التجميع، لكنك نادرًا ما تحتاج إليه.
تسجّل النقرة render 1 1 1 واحدًا، لا ثلاثة عروض. كانت ثلاثة تحديثات حالة داخل مهلة زمنية تعني ثلاثة عروض قبل React 18.
ماذا يسجّل هذا؟ (ترتيب التأثيرات)
اقرأ الكود واكتب الأسطر الأربعة التي ستطبعها وحدة التحكم، بالترتيب، قبل أن تنظر إليها.
يسير العرض من الأعلى إلى الأسفل: render App، ثم render Child. وتعمل التأثيرات من الأسفل إلى الأعلى، بعد إثبات الشجرة كلها: effect Child، ثم effect App. يستطيع تأثير الأب الاعتماد على أن تأثيرات أبنائه قد عملت بالفعل.
ما الفرق بين useMemo وuseCallback؟
يخزّن useMemo(() => compute(a, b), [a, b]) نتيجة عملية حسابية. ويخزّن useCallback(fn, [deps]) الدالة نفسها، فهو يعادل useMemo(() => fn, [deps]). ولا يفيد أي منهما إلا عندما يقارن شيء في الأسفل بالمرجع: ابن ملفوف بـ memo، أو مصفوفة اعتماديات خطاف آخر. ومنذ React Compiler 1.0 (أكتوبر 2025) تستطيع خطوة بناء إضافة هذا الحفظ تلقائيًا، لكن الحفظ اليدوي ما زال يعمل وما زلت ستراه في معظم المشاريع.
ماذا يفعل React.memo، ولماذا يحتاج غالبًا إلى useCallback؟
يتخطى memo(Component) إعادة عرض المكوّن عندما تكون props الخاصة به كما كانت في المرة السابقة، بمقارنة كل واحدة عبر Object.is. والدالة المكتوبة مضمّنة في الأب دالة جديدة في كل عرض، فتُحسب prop متغيرة وتُبطل memo. ويُبقي useCallback الدالة نفسها بين مرات العرض.
امسح وحدة التحكم وانقر "Re-render parent": لا يظهر إلا render inline handler. حصل الابن الآخر على الدالة نفسها فتُخطّي. احذف memo(...) حول Button فيسجّل الاثنان مع كل نقرة، ما يُظهر أن useCallback وحده لا يفعل شيئًا.
ما هو السياق، وما تكلفته؟
يمرر السياق قيمة إلى كل مكوّن تحت مزوّد دون تمريرها عبر props: createContext، ثم <ThemeContext value={theme}> في React 19 (أو <ThemeContext.Provider>)، وuseContext(ThemeContext) لقراءتها. والتكلفة أن كل مكوّن يقرأ السياق يُعاد عرضه عندما تتغير القيمة، ولا يوقف memo ذلك. اجعله رخيصًا بتقسيم القيم غير المترابطة إلى سياقات منفصلة، وتغليف القيمة الكائن بـ useMemo، وإبعاد الحالة سريعة التغير عن السياق على مستوى التطبيق. راجع useContext.
ما هي المراجع، ومتى تستخدم واحدًا بدلًا من الحالة؟
يُرجع useRef(initial) كائنًا تبقى خاصيته current بين مرات العرض، وتغييرها لا يطلق عرضًا. استخدم مرجعًا للقيم التي لا تعتمد عليها الشاشة (معرّف مؤقت، أو القيمة السابقة، أو علامة) وللوصول إلى DOM (<input ref={inputRef}> ثم inputRef.current.focus()). وإذا كانت القيمة تُعرض على الشاشة، فمكانها الحالة.
ما هو DOM الافتراضي، وما هو التوفيق؟
في كل عرض تبني React شجرة جديدة من العناصر وتقارنها بالسابقة؛ وهذه المقارنة هي التوفيق. إذا تغيّر نوع عنصر (أصبح div هو section، أو أصبح ProfileA هو ProfileB)، تدمّر React تلك الشجرة الفرعية وحالتها وتبني واحدة جديدة. وإذا كان النوع نفسه، تحتفظ بعقدة DOM وتحدّث props المتغيرة، وتستخدم المفاتيح لمطابقة الأبناء في القوائم. و«DOM الافتراضي» هو الاسم الشائع لتلك الشجرة في الذاكرة؛ والسرعة تأتي من لمس ما تغيّر فقط، لا من سرعة الشجرة نفسها.
ما هو الخطاف المخصص؟
دالة يبدأ اسمها بـ use وتستدعي خطافات أخرى، لتتمكن من إعادة استخدام المنطق ذي الحالة بين المكوّنات: useOnlineStatus وuseLocalStorage وuseDebouncedValue. كل مكوّن يستدعيها يحصل على حالته المستقلة؛ فالخطاف المخصص يشارك المنطق لا البيانات. راجع الخطافات المخصصة.
ماذا يفعل StrictMode؟
يفعّل <StrictMode> فحوصًا إضافية في التطوير فقط. يعرض المكوّنات مرتين ليكشف العرض غير النقي، ويشغّل إعداد التأثيرات ثم تنظيفها ثم إعدادها مرة عند التركيب ليكشف التنظيف المفقود، ويفعل الشيء نفسه لدوال المرجع، ويحذّر من الواجهات المُهملة. لا يحدث أي من هذا في بناء الإنتاج، فسجل وحدة التحكم المزدوج في التطوير متوقع وليس خطأ.
أسئلة المستوى المتقدم
ماذا يسجّل هذا؟ (العرض، وتأثير التخطيط، والتأثير)
توقع آخر. ثلاثة سجلات، في ثلاث لحظات مختلفة من العرض.
الترتيب هو العرض، ثم useLayoutEffect، ثم useEffect، رغم أن useEffect مكتوب أولًا. تعمل تأثيرات التخطيط بعد أن تحدّث React الـ DOM لكن قبل أن يرسم المتصفح، ولهذا تناسب قياس عنصر؛ أما التأثيرات العادية فتعمل لاحقًا، عادة بعد الرسم، وينبغي أن تكون الخيار الافتراضي.
ما هي حدود الأخطاء؟
مكوّن صنف يلتقط الأخطاء المرمية أثناء عرض أبنائه ويعرض بديلًا بدلًا من إزالة التطبيق كله. يحتاج إلى static getDerivedStateFromError (للتحول إلى البديل) وعادة إلى componentDidCatch (للتسجيل). وما زال لا يوجد مقابل بالخطافات، لذا تكتب الفرق في المشاريع المبنية على مكوّنات الدوال صنفًا واحدًا أو تستخدم الحزمة react-error-boundary.
انقر "Break it": يستبدل البديل أبناء الحدود فقط، ويبقى الزر، وما زالت React تبلّغ عن الخطأ الملتقط في وحدة التحكم. لا تلتقط الحدود الأخطاء في معالجات الأحداث، ولا في الكود غير المتزامن مثل setTimeout، ولا في الحدود نفسها. ولتلك تستخدم try/catch وتضبط الحالة.
ما هي البوابة (portal)؟
يعرض createPortal(children, domNode) من react-dom الأبناء داخل عقدة DOM مختلفة، عادة document.body، مع إبقائهم في المكان نفسه من شجرة React. تستخدمها النوافذ المنبثقة والتلميحات والإشعارات للإفلات من overflow: hidden أو سياق التراكب الخاص بالأب. ما زال السياق يعمل عبر البوابة، وتنتشر الأحداث عبر شجرة React، فتصل النقرة داخل البوابة إلى onClick الخاص بالأب رغم أن عقد DOM في مكان آخر. راجع البوابات.
ما هما Suspense وlazy؟
يحمّل lazy(() => import('./Chart')) كود المكوّن في أول مرة يُعرض فيها، ما يقسّم حزمتك. ويعرض <Suspense fallback={<Spinner />}> البديل ما دام أي شيء بداخله ما زال يُحمَّل: كود كسول، أو بيانات تُقرأ بـ use(promise) أو بإطار عمل يدعم 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؟
يعلّم تحديث حالة كغير عاجل. يتيح const [isPending, startTransition] = useTransition() وstartTransition(() => setTab('posts')) لـ React إبقاء الشاشة الحالية سريعة الاستجابة ومقاطعة العرض البطيء إذا وصل تحديث أكثر إلحاحًا، مثل ضغطة مفتاح. وتخبرك isPending بأن تعرض تلميحًا. وفي React 19 يمكن للدالة التي تمررها أن تكون غير متزامنة، وهو ما تسميه React إجراءً. لا تستخدمه لتحديث قيمة حقل نصي نفسها؛ فذلك التحديث يجب أن يكون عاجلًا.
ما هي مكوّنات خادم React؟
مكوّنات لا تعمل إلا على الخادم (أو وقت البناء) وترسل نتيجتها المعروضة إلى المتصفح، لا كودها أبدًا. يمكن أن تكون async وتقرأ قاعدة بيانات أو نظام الملفات مباشرة، لكنها لا تستطيع استخدام الحالة أو التأثيرات أو واجهات المتصفح. الملف المعلّم بـ 'use client' يبدأ حدود عميل تعيش فيها المكوّنات التفاعلية، و'use server' يعلّم دوال الخادم التي يستطيع كود العميل استدعاءها. تستخدمها عبر إطار عمل مثل Next.js مع App Router؛ أما تطبيقات Vite العادية فليس لها خادم يشغّلها.
ما الذي تغيّر في React 19؟
- الإجراءات: دوال غير متزامنة في الانتقالات، مع تولّي حالة الانتظار نيابة عنك. يستدعي
<form action={fn}>الدالةfnمعFormDataالخاص بالنموذج. - خطافات جديدة:
useActionStateلنتيجة النموذج وحالة انتظاره، وuseOptimisticللواجهة المتفائلة، وuseFormStatusفيreact-dom. use: يقرأ وعدًا (ويعلّق حتى يُحل) أو سياقًا، وخلافًا للخطافات الأخرى يمكن استدعاؤه داخل شرط.refكـ prop: تستقبل مكوّنات الدوالrefمثل أي prop أخرى، فلم يعدforwardRefمطلوبًا للكود الجديد. ويمكن لدوال المرجع أن تُرجع دالة تنظيف.<Context>كمزوّد، إضافة إلى وسوم<title>و<meta>التي ترتفع إلى رأس المستند.- ما حُذف:
ReactDOM.render، ومراجع النصوص، والسياق القديم، وdefaultPropsعلى مكوّنات الدوال (استخدم القيم الافتراضية للمعاملات).
// React 19: ref is a normal prop
function TextInput({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
في صفحة React 19 أمثلة قابلة للتشغيل لكل منها.
كيف تختبر مكوّنات React؟
باستخدام React Testing Library ومشغّل مثل Vitest أو Jest. تعرض المكوّن، وتجد العناصر كما يفعل المستخدم (بالدور أو التسمية أو النص)، وتتفاعل بـ user-event، وتتحقق مما هو ظاهر، لا من الحالة الداخلية. وهذا يُبقي الاختبارات صالحة عبر إعادة الهيكلة.
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، وسجّل التفاعل البطيء، واقرأ مخطط اللهب: يُظهر أي المكوّنات عُرضت، وكم استغرق كل منها، و(مع إعداد "record why each component rendered") السبب. الإصلاحات المعتادة، بالترتيب الذي تجربها به: انقل الحالة إلى الأسفل لتعتمد عليها مكوّنات أقل، ومرّر الأشجار الفرعية البطيئة على أنها children، واحفظ الجزء المكلف بـ memo وuseMemo وuseCallback، واعرض القوائم الطويلة افتراضيًا (virtualize)، وأجّل التحديثات الثقيلة بـ useTransition أو useDeferredValue. قِس الأداء في بناء إنتاج أو بناء خاص بالقياس؛ فبنيات التطوير أبطأ وتضخّم أوقات العرض.
لماذا ينبغي أن يكون المكوّن نقيًا؟
بالـ props والحالة والسياق نفسها، ينبغي أن يُرجع المكوّن JSX نفسها ولا يغيّر شيئًا خارجه أثناء العرض. قد تعرض React مكوّنًا أكثر من مرة، أو بغير ترتيب، أو ترمي عرضًا (الانتقالات وSuspense وStrictMode)، فالآثار الجانبية في الجسم مثل تعديل متغير أو إرسال طلب تعمل عددًا لا يمكن توقعه من المرات. مكان الآثار الجانبية معالجات الأحداث أولًا، والتأثيرات عندما لا يكون حدث قد سببها.
متى لا ينبغي استخدام تأثير؟
عندما يمكن حساب القيمة أثناء العرض (القوائم المرشحة، والأسماء الكاملة، والمجاميع)، وعندما يكون العمل ناتجًا عن فعل مستخدم (ضعه في معالج الحدث)، وعندما تريد إعادة ضبط الحالة عند تغيّر prop (استخدم key). التأثيرات للمزامنة مع شيء خارج React: اتصال شبكي، أو واجهة متصفح، أو عنصر من طرف ثالث. وسلاسل التأثيرات التي تضبط الحالة مصدر شائع لمرات العرض الإضافية والأخطاء.
الأسئلة الشائعة
ما هي React؟
React مكتبة JavaScript لبناء واجهات المستخدم من مكوّنات. تصف كيف يجب أن تبدو الشاشة للبيانات الحالية، وتحدّث React الـ DOM الحقيقي عندما تتغير تلك البيانات.
ما الفرق بين الحالة وprops؟
props مدخلات يمررها الأب إلى المكوّن، ويعاملها المكوّن كقيم للقراءة فقط. أما الحالة فبيانات يملكها المكوّن ويغيّرها بمرور الوقت بدالة ضبط، ما يجعل React تعيد عرضه.
ما هي الخطافات في React؟
الخطافات دوال تبدأ أسماؤها بـ use تتيح لمكوّن الدالة استخدام ميزات React: useState للحالة، وuseEffect للآثار الجانبية، وuseContext للسياق، وuseRef للقيم التي لا تطلق العرض، وغيرها.
ما هو DOM الافتراضي؟
هو اسم وصف React للواجهة في الذاكرة. في كل عرض تبني React وصفًا جديدًا، وتقارنه بالسابق، ولا تطبّق على الـ DOM الحقيقي إلا الفروق. تُسمى هذه المقارنة التوفيق (reconciliation).
لماذا تهم المفاتيح في قوائم React؟
يخبر المفتاح React أي عنصر في القائمة هو أي عنصر بين مرات العرض. مع مفاتيح ثابتة (مثل معرّف) تُبقي React حالة كل عنصر وعقدة DOM الخاصة به مرتبطتين بالبيانات الصحيحة عند إضافة العناصر أو حذفها أو إعادة ترتيبها. واستخدام فهرس المصفوفة يكسر ذلك عندما يتغير الترتيب.
ما هي JSX؟
JSX امتداد للصيغة يتيح لك كتابة ترميز شبيه بـ HTML داخل JavaScript. يحوّل المترجم كل وسم إلى استدعاء دالة ينشئ عنصر React، فيصبح <h1>Hi</h1> كائنًا عاديًا يصف h1.